拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。
以下情况都会调用拷贝构造函数:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。
自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。
浅拷贝和深拷贝
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。
浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。
代码实现:
拷贝构造:
#include <stdio.h>
class Test6_1
{
public:
Test6_1 (int a)
{
m_a = a;
printf ("普通构造函数\n");
}
// 拷贝构造
Test6_1(const Test6_1 &obj)
{
// m_a = obj.m_a;
m_a = -100;
printf ("拷贝构造被调用\n");
}
~Test6_1()
{
printf ("***********析构函数被调用: m_a = %d\n", m_a);
}
void print()
{
printf ("a = %d\n", m_a);
}
private:
int m_a;
};
// void test(Test6_1 &t)
void test(Test6_1 t)
{
t.print();
}
Test6_1 test6_1()
{
Test6_1 t(10);
return t;
}
// 函数返回值是一个对象
int main6_2()
{
// 一个函数返回一个对象的时候会创建一个匿名对象,拿返回的那个对象
// 对匿名对象进行初始化,会调用拷贝构造
// 如果没有去接收函数的返回值的话,匿名对象会立马被销毁
// test6_1();
// 如果用一个对象去接收函数的返回值,先用函数返回的对象去初始化
// 匿名对象,调用一次拷贝构造,然后拿新的对象的名字去命名这个匿名对象
// 匿名对象从无名转成有名
// Test6_1 t1 = test6_1();
Test6_1 t2(10);
// 用函数返回的对象对匿名对象进行初始化,调用拷贝构造
// 用匿名对象对 t2 进行赋值,调用 赋值运算符 =
t2 = test6_1();
// 初始化、构造、赋值
int a; // 定义变量
int b = 20; // 初始化
a = 10; // 赋值
printf ("-------------------------\n");
return 0;
}
int main6_1()
{
// 1、直接初始化
Test6_1 t1(10);
#if 0
Test6_1 t2(t1); // 拿 t1 取初始化 t2
// 2、等于号
Test6_1 t3 = t1;
t3.print();
#endif
// 3、当对象作为函数参数传递的时候会调用拷贝构造
test(t1);
return 0;
}
默认构造函数:
#include <stdio.h>
class Test7_1
{
public:
Test7_1()
{
}
Test7_1(int a)
{
m_a = a;
}
void print ()
{
printf ("m_a = %d\n", m_a);
}
private:
int m_a;
};
// 如果类中没有定义任何构造函数,编译器会自动生成一个无参构造函数,没有做任何事情
// 如果写了构造函数,编译器将不再提供默认的无参构造函数
// 如果还想进行无参构造,需要显示定义无参构造函数
// 如果没有定义拷贝构造函数,编译器会自动生成一个拷贝构造函数,
// 会做普通类型数据的复制
// 还会生成一个默认的 析构函数
class Test7_2
{
#if 0
Test7_2() {}
Test7_2(const Test7_2 &obj) {}
~Test7_2(){};
#endif
};
int main7_1()
{
Test7_1 b;
Test7_1 a(10);
Test7_1 c = a;
a.print();
c.print();
return 0;
}
深拷贝和浅拷贝:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 浅拷贝:在拷贝指针的时候只是拷贝了地址,不会进行空间的复制
class Test8_1
{
public:
Test8_1(int id, char *name)
{
m_id = id;
m_name = (char *)malloc(sizeof(char)*20);
strcpy (m_name, name);
}
~Test8_1()
{
if (m_name != NULL)
{
free (m_name);
m_name = NULL;
}
printf ("析构被调用**********\n");
}
void print()
{
printf ("id = %d, name = %s\n", m_id, m_name);
}
private:
int m_id;
// char m_name[20];
char *m_name;
};
// 深拷贝
class Test8_2
{
public:
Test8_2(int id, char *name)
{
m_id = id;
m_name = (char *)malloc(sizeof(char)*20);
strcpy (m_name, name);
}
// 自己写拷贝构造函数,避免浅拷贝
Test8_2(const Test8_2 &obj)
{
m_id = obj.m_id;
m_name = (char *)malloc(sizeof(char)*20);
strcpy (m_name, obj.m_name);
}
~Test8_2()
{
if (m_name != NULL)
{
free (m_name);
m_name = NULL;
}
printf ("析构被调用**********\n");
}
void print()
{
printf ("id = %d, name = %s\n", m_id, m_name);
}
private:
int m_id;
char *m_name;
};
class Test8_3
{
private:
// 将拷贝构造写成私有的函数,只需声明,不需要实现
Test8_3(const Test8_3 &ibj);
public:
Test8_3(int a)
{
m_a = a;
}
private:
int m_a;
};
int main8_3()
{
Test8_3 t(10);
// Test8_3 t1 = t;
return 0;
}
int main8_2()
{
Test8_2 t1(10, "wang");
t1.print();
// 调用自己写的拷贝构造函数,进行深拷贝
Test8_2 t2 = t1;
t2.print();
return 0;
}
int main8_1()
{
Test8_1 t1(10, "wang");
t1.print();
// 调用默认的拷贝构造函数
Test8_1 t2 = t1;
//t2.print();
return 0;
}