使用-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
。
我可以采用哪些良好做法来避免这种情况,因为我无法控制B
或A
的实施?有静态分析仪能够检测到这种情况吗? (我的scan-build
版本没有发现问题。)
答案 0 :(得分:2)
我会从B
和C
派生单独的类(甚至可能使用模板类)。
这些类将包含一个非引用成员,该成员将成为a
和b
引用的内容。
然后,我会在这些派生类中实现必要的复制构造函数/赋值运算符,以防止悬空引用。
(然后我与B
和C
的作者进行了强有力的对话。
答案 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
,以避免它参与自动转换,我认为这是您问题的一部分。