1

以下最小化程序在使用 编译时-O3可能会出现段错误-O2,但使用-O0(使用 clang 4.0)执行良好:

#include <iostream>

class A {
public:
  virtual void me() const { std::cerr << "hi!\n"; }
};

class B {
public:
  B(const A& a_) : a(a_) {}
  virtual void me() const { a.me(); }

private:
  const A& a;
};

class C {
public:
  C(const B& b_) : b(b_) {}
  void me() const { b.me(); }

public:
  const B& b;
};

int main() {
  C c = C(A());
  c.me();
}

原因是它是使用对从临时构造c.b的类的临时对象的引用进行初始化的。构造函数退出后,临时对象消失了,但对它的引用仍保留在.BAc.C()Bc.b

鉴于我不控制Bor的实施,我可以采用哪些良好做法来避免这种情况A?是否有能够检测到这种情况的静态分析仪?(我的版本scan-build没有发现问题。)

相关:检测对临时的悬空引用

4

2 回答 2

2

我会从Band派生单独的类C(甚至可能使用模板类)。

这些类将包含一个非引用成员,该成员成为所引用的a事物b

然后,我将在这些派生类中实现必要的复制构造函数/赋值运算符,以防止悬空引用。

(然后我会与Band的作者进行一次强有力的对话C)。

于 2017-09-15T07:20:56.560 回答
1

我个人不喜欢将成员变量作为引用。它们不能被复制或移动,并且类的用户必须确保对象比引用本身更长。一个例外可能是设计为仅用作临时对象的内部辅助类,例如仿函数。

用指针替换引用应该很简单,然后如果您还在构造函数中使用指针:

class C {
public:
  C(const A *a_) : a(a_) {}
private:
  const A *a;
};

...等等。如果类很大,你又懒又不想改成员,你可以改一下构造函数:

class C {
public:
  C(const A *a_) : a(*a_) {}
private:
  const A &a;
};

为了滥用这个类,正如 OP 所说,你必须编写如下内容:

C c = C(&A());

然后错误应该很明显:将指针指向临时是一个非常糟糕的主意!

PS:我会在explicit您的构造函数中添加一个,只是为了避免它参与自动转换,我认为这是您问题的一部分。

于 2017-09-15T16:00:03.397 回答