跳转至

拷贝构造函数

拷贝构造函数(copy constructor)

使用拷贝构造函数的几种情况

class Person
{

}
//调用普通的构造函数创建p1
Person p1;
//创建p2的时候用p1创建,,把p1拷贝复制成p2
Person p2(p1);
//这也是拷贝p1,p1拷贝变成了p3。
Person p3 = p1;

//这是p1赋值给p4,不是拷贝。这个叫赋值操作。
Person p4;
p4 = p1;

C++类如果没有写构造函数,C++会帮我们写构造函数,包括普通构造函数和拷贝构造函数。

拷贝构造函数:参数必须是类的引用,可以加上const。把所有的数据都拷贝过去。

自定义拷贝构造函数的情况

C++编程,创建class,用class创建对象的时候,一般情况使用通常的构造函数,特殊情况下,做对象的copy的时候,必须使用拷贝构造函数。

把son也拷贝过去

class Person
{
public:
    //构造函数
    Person(std::string xm, double ht, int a) : name(xm),height(ht),age(a)//初始化列表
    {
        std::cout << "normal constructor" << std::endl;
    }

    Person(const Person& p)
    {
        name = p.name;
        height = p.height;
        age = p.age;

        ///第一种
        ///拷贝的是地址 指针。这种是共用的一个son。浅复制。
//        son = p.son;

        ///第二种
        ///这种,拷贝的话son就是各自不同的。深复制。
        son = new Person(p.son->name, p.son->height, p.son->age);
        std::cout << "copy constructor" << std::endl;
    }

    void print()
    {
        std::cout << name << ", " << height << ", " << age << std::endl;
    }

//private:
    std::string name;
    int age;
    double height;

    Person *son;
};
//测试调用
void testCopy()
{
    //p1
    Person p1("张飞", 88.8, 48);
    p1.son = new Person("张鲍", 66.6, 6);
    p1.print();
    p1.son->print();

    //p2拷贝p1
    Person p2(p1);
    p1.age = 59;
    p1.print();
    p2.print();
    p2.son->age = 7;
    p1.son->print();
    p2.son->print();
}

修改p1的age,不影响p2,这两个互不影响。

浅复制只复制的指针,两个对象的son是同一个。

深复制是新的。两个对象的son是各自拥有的。

C++默认做的拷贝构造函数是浅复制。做深复制的时候需要我们自己写。

当有一个指针的数据成员的时候,C++自动写的复制构造函数只复制指针,需要我们自己写复制构造函数。

还有一种情况用到复制

参数的传递。按值传递,就是复制。调用对象的复制构造函数,结束之后会调用析构函数。所以按值传递的效率较低,因为它要调用复制构造函数。而且对象不使用了还要调用它的析构函数。

如果函数还有一个返回值。返回的时候还是复制。

Person f(Person p)
{
    p.print();
    return p;
}
//p1
Person p1("张飞", 88.8, 48);
p1.son = new Person("张鲍", 66.6, 6);
p1.print();
p1.son->print();

f(p1);

上面这种的效果比较低,最好都是传参传引用,返回值返回引用。就不需要调用复制构造函数和析构函数。

所以传引用或者指针,比传值要好很多。

Person& f(Person& p)
{
    p.print();
    return p;
}

静态的成员

用静态成员记录一共创建了多少个对象。

在构造函数中++ 在析构的时候--。

class HowMany
{
    static int objectCount;
public:
    HowMany() {objectCount++;}

    //用C++默认的复制构造函数,计数器不对,不会++。所以自己写复制构造函数。
    HowMany(HowMany& h)
    {
        objectCount++;
    }

    static void print(const string& msg = "")
    {
        if (msg.size() != 0) {
            cout << msg << ": ";
            cout << objectCount << endl;
        }
    }
    ~HowMany()
    {
        objectCount--;
        print("~HowMany");
    }

    //测试使用
    void foo(){}
};
//静态成员的定义写在外面。初始为0。
int HowMany::objectCount = 0;

写一个函数,供测试调用

  1. 按值传递

会有一个复制,所以计数也会加1。

函数结束之后,会调用析构,计数减减。

  1. 有返回值

也会调用一次复制构造函数。

return的时候调用一次复制构造函数,加加。return之后就析构了,再减减。

HowMany m(HowMany h)
{
    HowMany::print("inside m");
    h.foo();
    return h;
}

测试:

按值传递,会有一个复制,所以计数也会加1。

void testCopy()
{
    HowMany h1;
    HowMany::print("after construction of h1");
    HowMany h2;
    HowMany::print("after construction of h2");
    HowMany h3(h1);
    HowMany::print("after copy construction of h3");

    m(h1);
    HowMany::print("after call m()");
}

复制构造函数用处

参数传递的时候使用

参数传递:

  • 使用传值方式传递非常小的对象(内置基本类型)

不管是大的还是小的,只要是class类类型就会调用复制构造函数和析构函数,所以最好传常量引用。

  • 传递不需要修改的大对象(类类型),使用传常量引用方式。

  • 函数要修改参数原始对象,使用传引用方式(传指针更好)

禁止拷贝

写一个类 禁止复制,使用的时候就不可以按值传递,只能传引用。

复制构造函数写出私有的。

class Demo
{
private:
    Demo(Demo&);
public:
    Demo(){}
};

//测试调用
void testCopy()
{
    Demo d1;
    Demo d2;
//    Demo d3(d1);//报错,拷贝构造函数是私有的。
}

单例模式就把复制构造函数做成私有的,这样就不会复制有多个对象。

C++默认的拷贝构造函数

  1. 计数不会加1
  2. 对象如果有指针成员,默认的是浅复制,深拷贝的话需要自己写成深拷贝。