【问题标题】:C++ Seg fault on reference to stored base class pointer引用存储的基类指针的 C++ Seg 错误
【发布时间】:2010-02-02 00:15:17
【问题描述】:

我在以下代码中通过 g++ 编译器遇到了一些令人讨厌的分段错误。关于为什么会发生这种情况以及如何解决它的任何想法都会很棒。

#include <iostream>
using namespace std;

class Base {
public:
  Base() {}
  virtual ~Base() {};
  virtual int getNum(int) = 0;
};

class Derived: public Base {
public:
  Derived() :
    Base() {}
  ~Derived() {}

  int getNum(int num) {
    return num;
  }
};

class Foo {
public:
  Foo() {
  };
  void init() {
    Derived n;
    *baseId = n;
  }
  void otherStuff() {
    cout << "The num is" << baseId->getNum(14) << baseId->getNum(15) << baseId->getNum(16) << baseId->getNum(15) << endl;
  }
  Derived* baseId;
};

int main() {
  Foo f;
  f.init();
  f.otherStuff();
  return 0;
}

【问题讨论】:

  • 是因为你取消了baseId的引用吗?

标签: c++ inheritance g++ segmentation-fault


【解决方案1】:

这里:

void init() {
    Derived n;
    *baseId = n;
}

指针 baseId 永远不会初始化,当您取消引用它时会导致未定义的行为。在这里解释您要做什么可能是个好主意。如果你想维护一个指向 Derived 或 Base 但开始指向派生的指针,你可以说:

void init() {
    baseId = new Derived;
}

但是你可能需要一个复制构造函数、一个赋值运算符和一个析构函数来管理指针。

此外,出于多种原因,编写 init() 函数通常不是一个好主意 - 最好直接在构造函数或其初始化列表中完成工作。

【讨论】:

  • +1 提到 all 3 通常编译器提供的函数应该手动定义。
【解决方案2】:

当您调用 f.init() 时,FoobaseId 成员未初始化,但您在 init() 中取消引用它。你确定你不想要更多类似的东西:

baseId = new Derived()

【讨论】:

  • 我没有意识到 *baseId = n、baseId = &n 和 baseId = new Derived() 之间有区别
  • @Jon2029: *baseId = n 在将 n 分配给它之前取消引用 baseId。如果baseId 没有首先指向任何有效的地方,那么您将遇到分段错误。 baseId = &amp;n 仅在 n 存在时才有效。我不太了解C++,但我认为ninit() 退出后会失效。
  • 正确。函数返回后n消失,指针再次失效。
【解决方案3】:
  void init() {
    Derived n;
    *baseId = n;
  }

除了 Neil 指出的以外,派生的 n 是您的 init 函数的本地函数。当您退出该功能时,它会“死亡”,因此即使您正确分配了它,它也不会起作用。

你想要的不是在堆栈上分配,而是在堆上:

  void init() {
    baseId = new Derived();
  }

甚至更好:

  void init() {
    delete baseId;
    baseId = new Derived();
  }

还有一个析构函数和构造函数对来防止出现问题:

Foo() : baseId(0) {};
~Foo() { delete baseId; }

如果使用此方法,请确保阻止复制构造函数和赋值运算符,或正确实现它们。但是,要实现它们,您还需要实现对 Derived 的复制——或者最好:使用安全的 shared_ptr 来存储指针。

【讨论】:

  • 非常好。我不明白 Derived() 在堆栈上分配而在堆上分配新。谢谢你的提示。 (因为这些事情往往是这样,实际的代码要复杂得多)
  • 你不需要写if (baseId) delete baseId;,而可以简单地写delete baseId;(删除一个空指针是合法的,实际上是一个空操作)。
  • @R Samuel -- 我知道,请注意我确实在析构函数中考虑到了这一点 -- 不过为了完整起见,我这样写。
  • 如果我们见面,我会打你的头。我并不是要显得迂腐,但我已经被这样的课程伤害了很多次......这里是Sacred ThreeCopy ConstructorAssignment OperatorDestructor。如果您定义其中任何一个,请定义其他 2 个,或者去地狱(与您的开发人员同行)。
  • 不幸的是,我总是担心回复会被视为表面价值。并不是说我赞助复制/粘贴编码......但我认为最好将所有线索放在一个地方而不是分散在许多答案中:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多