c++显式说明符生成更安全的代码
c++ explicit specifier generate safer code
我一直在研究显式说明符,我希望我能有一些反馈(小的例子将是完美的):
- 显式构造函数如何防止副本初始化?
- 你能提供一个小的例子来说明在生成更安全的代码方面显式构造函数比隐式构造函数有多大的好处吗?
我不太关心显式转换函数(c++ 11),只关心一般的actor原则
如有任何反馈,将不胜感激
Thanks in advance
我的例子:
class MyClass {
public:
MyClass() = default;
// copy constructor
explicit MyClass(const MyClass& other) = default;
// conversion constructor
explicit MyClass(int val) : val_(val) {}; // constructor that creates a MyClass object from an int.. in other words this 'converts' an int into a MyClass object
private:
int val_;
};
void foo(MyClass n) {};
int main() {
MyClass obj;
MyClass obj2 = obj; // implicit use of the copy constructor - does not compile if the copy ctor is marked explicit
MyClass obj3(obj); // explicit call to the copy ctr... always works
foo(obj); // regular call to foo, copy ctor called
foo(2); // implicit use of the conversion ctr - does not compile if the conversion ctor is marked explicit.
// this automatic conversion could be risky/undesired in some cases.. therefore the need for the explicit keyword.
return 0;
}
以1.
为例:
struct Foo{};
struct Bar {
explicit Bar(Foo){}
};
int main() {
Bar a {Foo{}}; // compiles
Bar b = Foo{}; // error
}
a
可以编译,因为它使用直接初始化,所以可以使用显式构造函数。b
不编译,因为它使用拷贝初始化。
显式构造函数防止意外地执行隐式转换:
void oops (const Bar&) {
}
int main() {
oops (Foo{});
}
从传入的Foo
构造一个临时的Bar
,并将其绑定到引用参数。这在很大程度上是对用户隐藏的,如果构造开销很大,或者您期望实参和形参是相同的对象,则可能会导致令人惊讶的结果。
从复制初始化的cppreference页面:
复制初始化比直接初始化更宽松:
explicit
构造函数不是转换构造函数,不考虑复制初始化。
所以你第一个问题的答案是"设计"。复制初始化只考虑隐式转换。
至于为什么要禁用隐式转换:它是在构造可能危险的对象时用作安全措施的,因此应该是显式的(在代码中可见)。
例如,假设std::unique_ptr<T>
的T*
的构造函数是隐式的。现在让我们看看一些(人为的)用户代码:
void forbulate(Foo * const myFoo) {
myFoo->doStuff();
lookAt(myFoo); // My Foo is amazing
myFoo->doMoreStuff();
}
乍一看这很好。但你不知道的是,lookAt
实际上有以下原型:
void lookAt(std::unique_ptr<Foo> yourFoo);
所以我们实际上是在无声地构造一个unique_ptr<Foo>
来传递给lookAt
。这意味着一些非常重要的事情:这个unique_ptr
现在是myFoo
, 的所有者,并将在破坏时杀死它。实际情况是:
void forbulate(Foo * const myFoo) {
myFoo->doStuff(); // Fine
lookAt(myFoo) // Still fine
; // myFoo is `delete`d !
myFoo->doMoreStuff(); // Ouch, undefined behaviour from accessing a dead object
} // Sorry caller, I accidentally your Foo :(
注意,删除本身可能已经是UB了(例如,如果myFoo
是自动实例的地址)。无论如何,这段看起来无害的代码实际上是一颗地雷。不要到处散布地雷,这就是为什么我们有explicit
构造函数:在实际的unique_ptr
规范中,代码甚至无法编译,你知道哪里出了问题。
相关文章:
- 提供对不同类型的数据(建议、代码审查)的线程安全访问的类
- 实现在多线程代码中安全恢复的断点
- C++代码中的异常安全
- 编写"anti-lack of memory"异常安全代码
- 在代码的其他部分中对lock_gard和不使用相同的互斥锁是否安全?
- 我怎么知道C++编译器是否制作线程安全的静态对象代码
- 此代码安全吗?(链表,C++)
- 从 C# 到C++和返回的数组,没有不安全的代码
- 此代码是否容易受到 SQL 注入的攻击?我该如何使其安全
- 如何改进代码以生成安全的随机数
- 苹果安全传输代码错误
- 这段代码安全吗(为什么它没有崩溃)?
- 什么是 C# 安全代码在C++中的等效项
- 有没有更好的方法可以使此代码线程安全?线程局部静态似乎是一个生硬的工具
- 此代码是否对C++线程安全
- 如何使用 openMP 使此代码线程安全
- C 代码 - 覆盖率(或其他静态代码分析仪) 线程安全
- 异常安全代码和移动语义
- 在单元安全代码中处理文本零
- 泛型堆栈类的异常安全代码