在编译时检测或避免对临时的死引用

时间:2017-09-15 07:16:46

标签: c++ static-analysis c++03 clang-static-analyzer dangling-pointer

使用-O3-O2编译时,以下minimal-ish程序段错误,但使用-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初始化时引用了一个由临时B构造的类A的临时对象。构造函数c.C()退出后,临时B已消失,但对它的引用仍保留在c.b

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

相关:Detect dangling references to temporary

2 个答案:

答案 0 :(得分:2)

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

这些类将包含一个非引用成员,该成员将成为ab引用的内容。

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

(然后我与BC的作者进行了强有力的对话。

答案 1 :(得分: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,以避免它参与自动转换,我认为这是您问题的一部分。