c ++显式说明符生成更安全的代码

时间:2016-10-26 08:45:31

标签: c++ c++11

我一直在研究显式说明符,我希望我能得到一些反馈(小例子将是完美的):

  1. 显式构造函数如何阻止复制初始化?
  2. 您能否提供一个小例子,说明在生成更安全的代码方面,显式如何对隐式构造函数有很大的好处?
  3. 我不是那么关心显式转换函数(C ++ 11),只是一般的ctor原则

    非常感谢任何反馈

    提前致谢

3 个答案:

答案 0 :(得分:1)

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{});
}

这将从传入的Bar构造一个临时Foo并将其绑定到引用参数。这几乎是对用户隐藏的,如果构造很昂贵或者您希望参数和参数是同一个对象,则可能会导致令人惊讶的结果。显式构造函数可以保护您免受此问题的影响。

答案 1 :(得分:1)

来自cppreference page on copy-initialization

  

复制初始化比直接初始化更不宽容:explicit构造函数不转换构造函数,不考虑复制初始化。

所以第一个问题的答案是“按设计”。复制初始化仅考虑隐式转换。

至于为什么要禁用隐式转换:在构造对象时可能会将其用作安全措施,因此应该是显式的(在代码中可见)。

例如,让我们假设来自std::unique_ptr<T>的{​​{1}}构造函数是隐含的。现在让我们看看一些(人为的)用户代码:

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

请注意,删除本身可能已经是UB(例如,如果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 :( 是自动实例的地址)。无论如何,这种看似无害的代码实际上是一种地雷。不到处传播地雷是我们拥有myFoo构造函数的原因:使用实际的explicit规范,代码甚至不编译,你知道什么是错的。

答案 2 :(得分:1)

我的例子:

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;
}