在编译时检测或避免对临时文件的无效引用
Detect or avoid dead references to temporary on compile time
以下最小程序在使用 -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
是用 class B
的临时对象的引用初始化的,该临时对象是从临时 A
构造的。构造函数 c.C()
退出后,临时 B
消失了,但对它的引用仍在 c.b
.
中
鉴于我无法控制 B
或 A
的实施,我可以采用哪些好的做法来避免这种情况?是否有能够检测到这种情况的静态分析仪? (我的scan-build
版本没有发现问题。)
相关:
我会从 B
和 C
派生出单独的 classes(甚至可能使用模板 class)。
这些 classes 将包含一个非引用成员,它成为 a
和 b
所引用的东西。
然后我会在这些派生的 classes 中实现必要的复制构造函数/赋值运算符,以防止悬空引用。
(然后我会与 B
和 C
的作者进行热烈的对话)。
我个人不喜欢将成员变量作为引用。它们不能被复制或移动,并且 class 的用户必须确保对象比引用本身长。一个例外可能是内部辅助 class 设计用作临时对象,例如函子。
用指针替换引用应该很简单,然后如果你在构造函数中也使用指针:
class C {
public:
C(const A *a_) : a(a_) {}
private:
const A *a;
};
...等等。如果class很大,懒得改成员,可以直接改构造函数:
class C {
public:
C(const A *a_) : a(*a_) {}
private:
const A &a;
};
为了滥用此 class,正如 OP 所说,您必须编写如下内容:
C c = C(&A());
然后错误应该很明显:将指针指向临时对象是一个非常糟糕的主意!
PS:我会在你的构造函数中添加一个explicit
,只是为了避免它参与自动转换,我认为这是你问题的一部分。
以下最小程序在使用 -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
是用 class B
的临时对象的引用初始化的,该临时对象是从临时 A
构造的。构造函数 c.C()
退出后,临时 B
消失了,但对它的引用仍在 c.b
.
鉴于我无法控制 B
或 A
的实施,我可以采用哪些好的做法来避免这种情况?是否有能够检测到这种情况的静态分析仪? (我的scan-build
版本没有发现问题。)
相关:
我会从 B
和 C
派生出单独的 classes(甚至可能使用模板 class)。
这些 classes 将包含一个非引用成员,它成为 a
和 b
所引用的东西。
然后我会在这些派生的 classes 中实现必要的复制构造函数/赋值运算符,以防止悬空引用。
(然后我会与 B
和 C
的作者进行热烈的对话)。
我个人不喜欢将成员变量作为引用。它们不能被复制或移动,并且 class 的用户必须确保对象比引用本身长。一个例外可能是内部辅助 class 设计用作临时对象,例如函子。
用指针替换引用应该很简单,然后如果你在构造函数中也使用指针:
class C {
public:
C(const A *a_) : a(a_) {}
private:
const A *a;
};
...等等。如果class很大,懒得改成员,可以直接改构造函数:
class C {
public:
C(const A *a_) : a(*a_) {}
private:
const A &a;
};
为了滥用此 class,正如 OP 所说,您必须编写如下内容:
C c = C(&A());
然后错误应该很明显:将指针指向临时对象是一个非常糟糕的主意!
PS:我会在你的构造函数中添加一个explicit
,只是为了避免它参与自动转换,我认为这是你问题的一部分。