检测或避免在编译时

检测或避免在编译时

问题描述:

临时死引用的与-O3-O2编译也许当以下最低十岁上下的程序段错误,但-O0执行罚款(与铿锵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被初始化到从一个临时A构造B类的临时对象的引用。构造c.C()退出后,临时B走了,但对它的引用仍然c.b

我可以采用什么好的做法,以避免出现这种情况,因为我无法控制的BA执行?是否有静态分析仪能够检测到这种情况? (我的scan-build版本没有发现问题。)

相关:Detect dangling references to temporary

+0

好,U现在,右值引用。它们只绑定到临时对象。我想也许这可以作为一个只有左值的ref-wrapper抽象出来。 –

+0

通常,不要声明类型引用的成员变量。改用指针。如果你在构造函数中得到一个指针,结果代码将会很明显。 – rodrigo

+0

@rodrigo:这听起来很有趣。我想这也可以工作,如果我只声明构造函数参数作为指针,然后初始化引用?我宁愿不重写所有使用引用的代码... – krlmlr

我个人不喜欢有成员变量的引用。它们不能被复制或移动,并且该类的用户必须确保该对象超出参考本身。一个异常可能是一个内部辅助类,它被设计成只用作临时对象,比如函子。

更换一个指针引用应该是简单的,然后如果你也是在构造函数中使用指针:

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,只是为了避免它参与自动转换,我认为这是自己的问题的一部分。

+0

谢谢。我不能改变'B'或'A'类,如果你的意思是加上'explicit'。 – krlmlr

+0

@krimir不,我的意思是增加一个字面的'explicit C(const A * a_)',这样构造函数就不会作为类型转换的一部分被隐式调用。意外的调用你的构造函数 – rodrigo

我就从BC获得单独的类(甚至可能使用模板类)。

这些类将含有变成ab指东西的非基准部件。

我然后实现这些派生类所需的拷贝构造函数/赋值运算符,以防止悬空引用。

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

+0

这是一个现有的代码库,有什么方法可以找到这种反模式的所有用途? – krlmlr