实例化类的正确方法

The correct ways about Instantiating class

本文关键字:方法 实例化      更新时间:2023-10-16

这两种实例化类的方式都是可以接受的吗?我的意图是在堆栈上而不是堆上创建CBox

CBox a=CBox(1);
CBox b(2);

CBox的定义如下:

class CBox
{
public:
    int abc;
    CBox(int var){
        abc=var;
    }
};

实际上,由于在第一个定义中省略了复制/移动构造函数,这两个定义是等效的。

CBox a=CBox(1);
CBox b(2);

唯一的区别是,如果复制/移动构造函数不可访问或可能不会被隐式调用(例如,它是私有的或定义为已删除的或具有显式的函数说明符),那么第一条语句将不会被编译,除非MS vc++有自己的语言扩展,通常只是编译器错误。

如果你有一个这样的类定义:

class A
{
public:
    int q;
    A() {
        std::cout << "Constructor without argument" << std::endl;
    }
    A(int x) : q(x) {
        std::cout << "Constructor" << std::endl;
    }
    A(const A& a) {
        std::cout << "Copy Constructor" << std::endl;
        this->q = a.q;
    }
    A& operator=(const A& a) {
        std::cout << "Assignment operator" << std::endl;
        if(&a != this) this->q = a.q;
        return *this;
    }
};

则得到以下结果:

int main(int argc, char** argv)
{
    A a(2); // uses constructor
    A b = A(3); // uses constructor (exact same as A b(3);)
    A c = b; // uses copy constructor
    A d; // uses constructor without parameter
    c = d; // uses assignment operator
}

作为注释:在c++ 11中,您还可以编写A a{2};,以避免与函数调用混淆。

所以总结一下:只要写A a(2); -或者如果你使用c++ 11 A a{2};

标准的相关部分为(n3797)8.5/17。

否则(即,对于剩余的复制初始化情况),可以从源转换的用户定义转换序列类型为目标类型或(当使用转换函数时)的派生类,如13.3.1.4所述,并通过过载解析(13.3)选择最佳的一个。如果转换不能完成或有歧义,初始化是不规范的。选定的函数用初始化器调用表达作为它的论据;如果函数是构造函数,则调用类的非限定版本的临时对象初始化目的地类型。临时值是一个右值。调用的结果(这是构造函数情况下的临时)然后用于根据上面的规则,直接初始化对象拷贝初始化的目标。在某些情况下,一个允许实现消除其中固有的复制通过直接构造中间结果进行直接初始化被初始化的对象;参见12.2、12.8。

意思是,给定代码:

CBox a=CBox(1);
CBox b(2);
如果满足某些(合理的)条件,这两段代码将在符合标准的编译器中执行完全相同的操作。没有临时生成,没有赋值操作,也没有复制。在这两种情况下,调用构造函数直接初始化对象本身。

我发现标准不清楚这是否是copy elision,因为这里没有使用该术语。在12.8/31中有涉及。

我本来不想回答这个问题的,但不幸的是,其他的答案都是错的。我希望@Kerrek能同意,或者建议编辑。

在回答这个问题时,使用哪个取决于可读性和其他因素。