拷贝构造函数¶
拷贝构造函数(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。
函数结束之后,会调用析构,计数减减。
- 有返回值
也会调用一次复制构造函数。
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
- 对象如果有指针成员,默认的是浅复制,深拷贝的话需要自己写成深拷贝。