【问题标题】:Segfault when converting vector into vector<vector<complex>>将向量转换为向量<vector<complex>>时的段错误
【发布时间】:2021-04-09 05:34:48
【问题描述】:

美好的一天。我想知道我是否可以得到一些帮助。我有以下内容:

#include <vector>
#include <complex>
#include <iostream>

using messageScalar = std::complex<double>;
using messageVector = std::vector<messageScalar>;
using messageMatrix = std::vector<messageVector>;

class Tester {
 public:
  Tester(messageVector t) {
      messageMatrix container(1, t);
      messages = &container;

  }

  Tester(messageMatrix t) {
      messages = &t;

  }
  void debug() {
      std::cout << (*messages).size() << std::endl;
      for (auto &vector: *messages) {  // <- Debugger # 1
          for (auto &scalar: vector) {  // <- Debugger # 2

              std::cout << scalar << std::endl;
          }
      }
  }

 private:
  messageMatrix *messages = nullptr;

};

int main() {
    messageMatrix cMatrix = {{1, 2, 3}, {3, 4, 5}};
    Tester first(cMatrix);
    first.debug();
}

最后,我遇到了一个段错误。它告诉我我有 2 个条目(我期望 - “行”的数量),但我不清楚为什么会发生段错误。

2
18446744072976776191

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

当在 #1 使用调试器时,我返回一个变量“this”,我尝试打开它并返回

无法访问地址 X 的内存

当我将调试器移到下一行时,它的条目显然比我为该行输入的 (3) 多。

我是否遗漏了一些明显的东西?我也试过做一个

Tester(messageVector t) {
      messageMatrix container;
      container.emplace_back(t);
      messages = &container;

  }

但这也不起作用(不是应该,但我要疯了)

【问题讨论】:

  • messages 指向一个在构造函数作用域结束时被销毁的局部变量。
  • 您尝试过的所有内容都不是有效的 C++。当构造函数/方法/函数返回时,在任何构造函数、类方法或函数中本地声明的对象都会被销毁。保存指向它的指针,push_back-ing 指向它的指针,这些都没有任何区别。 C++ 不能以这种方式工作。 A good C++ textbook 应该会给你一个更完整的解释和教程,说明 C++ 中自动和动态范围对象之间的区别。
  • 是否有快速修复或我需要重组所有内容?
  • C++ 中很少有问题可以“快速修复”。您需要正确理解对象生命周期在 C++ 中是如何工作的。此外,只有您确切知道何时需要创建程序中的对象、它们的所有者以及它们何时被销毁。没有一个所有 C++ 程序都必须遵循的通用规则。这完全取决于每个 C++ 程序的独特要求和功能。您需要为您的 C++ 程序中的所有对象正确定义对象生命周期,并相应地实现它们。

标签: c++ pointers matrix vector segmentation-fault


【解决方案1】:

问题

问题出在您的构造函数中,请参阅 cmets:

  messageMatrix container(1, t);   // <--- container is a local variable
  messages = &container;           // <--- you take the address of the local variable
}                                  // <--- you leave constructor: local is destroyed

或者对于第二个构造函数:

  messages = &t;                   // <--- t is a parameter
}                                  // <--- you leave constructor, t is destroyed

在这两种情况下,一旦构建结束,messages 就会指向一个被破坏的对象。取消引用它是UB。任何事情都有可能发生。

解决方案

为了让代码更健壮,最简单的方法就是克隆消息矩阵:

class Tester {
 public:
  Tester(const messageVector &t) : messages(1,t) {
  }

  Tester(const messageMatrix &t) : messages(t) {
  }

  void debug() {
      std::cout << messages.size() << std::endl;
      for (const auto &vector: messages) {  // <- Debugger # 1
          for (const auto &scalar: vector) {  // <- Debugger # 2
              std::cout << scalar << std::endl;
          }
      }
  }

 private:
  messageMatrix messages; // local copy 

};

【讨论】:

  • 啊,谢谢!我试图将消息保留为指针(以检查 nullptr)。那可能吗?我还需要尝试一下,看看这个方法是否也适用于 messageScalar 类型
  • @IanQuah 可以保留一个指针,但您必须确保 1)您使用原始对象的地址; 2)在Tester存在期间原始对象存在且未移动; 3)原始对象在不再需要时被销毁,但在 Tester 被销毁之前不会被销毁。 messageVector 的情况使这很棘手,因为您需要构造一个临时矩阵。为此,您可以使用 shared_ptr,但您将不再使用原始向量(即,如果它发生变化,则临时矩阵不会更新)。
  • 抱歉,您能否详细说明我将如何使用 shared_ptr?您所说的其他所有内容(原始地址,原始地址未移动,原始未销毁之前)对我来说很有意义。话虽如此,我还不清楚 shared_ptr 如何以及为什么会有所帮助?此外,如果对原始文件的更改没有反映在临时文件中,那也很酷
  • @IanQuah 参见 shared_ptrmake_shared 使用共享指针。请参阅rule of 3,了解为什么共享指针优于原始指针的一些提示。如果可以不跟随对原始的更改,非指针版本会安全得多。从性能的角度来看,您的版本无论如何都会制作 1 或 2 个原始数据的副本。建议的替代方案只有一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2015-07-27
  • 2012-12-24
  • 2013-09-02
相关资源
最近更新 更多