首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >c++显式说明符生成更安全的代码

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

Stack Overflow用户
提问于 2016-10-26 08:45:30
回答 3查看 113关注 0票数 0

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

  1. 显式构造函数如何防止副本初始化?
  2. 您能提供一个很小的例子,说明在生成更安全的代码方面,显式比隐式构造函数有多大的好处吗?

我不太关心显式转换函数(C++11),只是一般的ctor原理

如有任何反馈,我们将不胜感激。

提前感谢

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-10-26 09:44:48

我的例子:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2016-10-26 08:55:53

1.的一个例子

代码语言:javascript
复制
struct Foo{};
struct Bar {
    explicit Bar(Foo){}    
};

int main() {
    Bar a {Foo{}}; // compiles
    Bar b = Foo{}; // error
}

a编译是因为它使用直接初始化,因此可以使用显式构造函数。b不编译,因为它使用复制初始化。

显式构造函数防止意外执行隐式转换:

代码语言:javascript
复制
void oops (const Bar&) {

}

int main() {
    oops (Foo{});
}

这将从传入的Bar中构造一个临时Foo,并将其绑定到引用参数。这在很大程度上是隐藏在用户面前的,如果构造很昂贵,或者您希望参数和参数是相同的对象,则可能会产生令人惊讶的结果。显式构造函数保护您不受此问题的影响。

票数 1
EN

Stack Overflow用户

发布于 2016-10-26 09:08:46

来自复制初始化时的cp首选项页

复制初始化不像直接初始化那样允许:explicit构造函数不是转换构造函数,也不考虑复制初始化。

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

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

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

代码语言:javascript
复制
void forbulate(Foo * const myFoo) {
    myFoo->doStuff();
    lookAt(myFoo); // My Foo is amazing
    myFoo->doMoreStuff();
}

乍一看这很好。但你不知道的是,lookAt实际上有以下原型:

代码语言:javascript
复制
void lookAt(std::unique_ptr<Foo> yourFoo);

因此,实际上,我们正在悄悄地构建一个unique_ptr<Foo>,以传递给lookAt。这意味着一件非常重要的事情:这个unique_ptr现在是myFoo的所有者,并将在销毁时杀死它。实际上发生的是:

代码语言:javascript
复制
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规范,代码甚至不编译,而且您知道出了什么问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40257643

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档