C++拷贝构造函数与拷贝赋值运算符

拷贝构造函数

拷贝构造函数定义了当用同类型的另一个对象初始化新对象时做什么,如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。拷贝构造函数的参数类型最好是const 引用。

如果我们没有为一个类定义拷贝构造函数,编译器会为我们生成一个,与合成默认构造函数不同,即使我们定义了其它构造函数,编译器也会为我们合成一个拷贝构造函数。

一般情况下,合成的拷贝构造函数会将其参数的成员逐个拷贝到正在创建的对象中,对于类类型成员,会使用其拷贝构造函数来拷贝;内置类型的成员则直接拷贝;数组类型的成员会逐个拷贝数组元素。

直接初始化与拷贝初始化

当使用直接初始化时,编译器使用与参数最匹配的构造函数进行初始化。当使用拷贝初始化时,编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换。

拷贝初始化通常使用拷贝构造函数来完成,但是有时也会使用移动构造函数。

std::string str1(10,'h');//直接初始化
std::string str2(str1);//直接初始化
std::string str3 = "hello";//拷贝初始化
std::string str4 = std::string(10, 'h');//拷贝初始化

什么情况下会发生拷贝初始化

  • 使用=定义变量时。
  • 将一个对象作为实参传递给一个非引用类型的形参时,这也解释了为什么拷贝构造函数自己的参数必须是引用类型,如果其参数不是引用类型,则会陷入死循环。
  • 从一个返回类型为非引用类型的函数返回一个对象时。
  • 用花括号列表初始化一个数组中的元素或以个聚合类中的成员时。
  • 标准库容器调用insert或push添加成员时。

拷贝赋值运算符

拷贝赋值运算符定义将一个对象赋予同类型的另一个对象时做什么,与拷贝构造函数一样,如果类未定义自己的拷贝赋值运算符,编译器会生成一个。

通过重载赋值运算符可以定义拷贝赋值运算符,为了与内置类型的赋值保持一致,赋值运算符返回一个指向左侧运算对象的引用。

class Person
{
public:
    const char* name_;
    Person(const char* name):name_(name) {};
    Person& operator=(const Person& person);
};
 
Person& Person::operator=(const Person& person)//拷贝赋值运算符
{
    std::cout << "call Person::operator=" << std::endl;
    name_ = person.name_;
    return *this;
}
 

int main(void)
{
    Person person1("xiao hong");
    Person person2("xiao ming");
    person1 = person2;
    system("pause");
    return 0;
}

需要析构函数的类也需要拷贝和赋值操作

当一个类需要析构函数时,那么也需要拷贝和赋值操作,原因是如果数据成员会在析构函数中被释放(例如一个new出来的指针),那么在进行拷贝或赋值时它也会被复制一份,当执行析构函数时,则会被释放两次。

需要拷贝操作的类也需要赋值操作,反之亦然

假设我们需要一个拷贝构造函数为每个新创建的对象生成一个独一无二的序号,那么肯定也需要在赋值操作时避免将序号赋予给对象。所以如果一个类需要拷贝构造函数,那么这个类大多数情况下也需要一个赋值运算符。

使用=default

和构造函数一样,我们也可以用=default显式要求编译器生成合成的拷贝构造函数和合成的赋值运算符,同样的如果=default出现在类内声明里,则它是内联的,类外定义中则不是。

class Person
{
public:
    const char* name_;
    Person(const char* name):name_(name) {};
    Person(const Person& person) = default;
    Person& operator=(const Person& person)=default;
};

你可能感兴趣的:(C++拷贝构造函数与拷贝赋值运算符)