拷贝构造函数
- 拷贝构造函数是构造函数的一种;
- 当利用一个已经存在的对象创建一个新的对象(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化;
- 拷贝构造函数的格式是固定的,以
const引用
作为参数,注意是引用reference
,来接收传进来的对象,利用传进来的对象,创建出一个新的对象;
- 如果不自定义拷贝构造函数,会调用C++系统默认的拷贝构造函数,如果自定义了拷贝构造函数,就调用自定义拷贝构造函数;
- 针对
成员变量是基本数据类型
的,使用系统默认的拷贝构造函数就可以了,能将参数对象的所有成员变量的数据全部拷贝到新创建对象对应的成员变量中;
- 针对
成员变量是引用,指针类型的
,必须自定义拷贝构造函数,实现深拷贝,而C++系统的数据拷贝默认是浅拷贝,会出现内存问题的,下面将会有详细的案例描述;
#include <iostream>
using namespace::std;
class Car {
int m_price;
int m_length;
public:
Car(int price = 0,int length = 0) : m_price(price),m_length(length){
cout << "Car(int price = 0,int length = 0)" << endl;
}
//1.拷贝构造函数 格式是固定的
Car(const Car &car){
this->m_price = car.m_price;
this->m_length = car.m_length;
cout << "Car(const Car &car)" <<endl;
}
//2.与系统的拷贝构造函数 功能相同 初始化列表写法
Car(const Car &car) : m_price(car.m_price),m_length(car.m_length){
cout << "Car(const Car &car)" <<endl;
}
void display(){
cout << "price = " << this->m_price << endl;
cout << "length = " << this->m_length << endl;
}
};
int main(int argc, const char * argv[]) {
Car car2(100,10);
//利用car2对象创建了car3,会调用car3对象的拷贝构造函数进行初始化
Car car3(car2);
car3.display();
//创建了car6对象,等价于Car car4(car2) 拷贝构造函数
Car car4 = car2;
car4.display();
Car car5;
//这里是赋值操作,没有创建新对象,所以不会调用拷贝构造函数
car5 = car2;
cout << "&car2 = " << &car2 << endl;
cout << "&car3 = " << &car3 << endl;
cout << "&car4 = " << &car4 << endl;
return 0;
}
#include <iostream>
using namespace::std;
class Person {
int m_age;
public:
//构造函数
Person(int age = 0) : m_age(age){ }
//拷贝构造函数
Person(const Person &person) : m_age(person.m_age){ }
};
class Student : public Person {
int m_score;
public:
//构造函数
Student(int age = 0,int score = 0) : Person(age),m_score(score){ }
//拷贝构造函数 调用父类的拷贝构造函数 初始化成员变量m_age
Student(const Student &student) : Person(student),m_score(student.m_score){ }
};
int main(int argc, const char * argv[]) {
return 0;
}
浅拷贝,深拷贝
- C++编译器默认提供的拷贝是浅拷贝;
- 将一个对象中的所有成员变量的值拷贝到另一个对象;
- 如果某个成员变量是指针,只会拷贝指针中所存储的地址值,并不会拷贝指针所指向的内存空间;
- 可能会导致堆空间的多次释放问题;
- 如果需要实现深拷贝,需要自定义拷贝构造函数;
- 将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间;
class Car {
public:
int m_price;
char *m_name;
//构造函数
Car(int price = 0,char *name = NULL) : m_price(price){
cout << "Car(int price = 0,char *name = NULL)" << endl;
//字符串要单独处理
if (name == NULL) return;
//申请堆空间 存储字符串内容
this->m_name = new char[strlen(name)+1]{};
//拷贝字符串内容到堆空间
strcpy(this->m_name, name);
}
//拷贝构造函数
Car(const Car &car) : m_price(car.m_price){
cout << "Car(const Car &car)" << endl;
//字符串要单独处理
if (car.m_name == NULL) return;
//申请堆空间 存储字符串内容
this->m_name = new char[strlen(car.m_name)+1]{};
//拷贝字符串内容到堆空间
strcpy(this->m_name, car.m_name);
}
~Car(){
cout << "~Car()" << endl;
if (this->m_name == NULL) return;
delete [] this->m_name;
this->m_name = NULL;
}
void display(){
cout << "price = " << this->m_price << endl;
cout << "name = " << this->m_name << endl;
}
};
int main(int argc, const char * argv[]) {
char name[] = {'b','m','w','\0'};
//堆对象
Car *car = new Car(100,name);
cout << car->m_price << endl;
cout << car->m_name << endl;
delete car;
//栈对象
Car car1(100,"bmw");
Car car2 = car1;
return 0;
}
- 针对堆对象的
构造函数
,需要将name字符串数组的内容从栈区拷贝到堆区,因为栈区的name可能会随时被释放掉,所以构造函数
在初始化字符串成员变量时要特殊处理,拷贝字符串内容到新的堆空间中;
- 针对栈对象的
拷贝构造函数
,原理图如下所示:
- 系统默认的
拷贝构造函数
是浅拷贝,那么拷贝的新对象,都指向同一块内存name,所以需自定义拷贝构造函数
,实现name的深拷贝,保证每一个car对象都有自己独立的name;否则会造成name的多次释放以及修改一个car对象,其他car对象也会变动的问题;
- 析构函数,针对name的堆空间也要做释放处理;
对象类型与返回值
- 使用对象类型作为函数的参数或者返回值时,会产生一些不必要的中间对象;
#include <iostream>
using namespace::std;
class Car {
int m_price;
public:
Car(int price = 0) : m_price(price){
cout << "Car(int price = 0)" << this << "-" << m_price << endl;
}
Car(const Car &car) : m_price(car.m_price){
cout << "Car(const Car &car)" << this << "-" << m_price << endl;
}
};
//对象形参
void test(Car car){
}
int main(int argc, const char * argv[]) {
Car car1(10);
//传参的 会产生一个新的对象 因为拷贝构造函数
test(car1);
return 0;
}
-
test(car1)
,car1传递给形参car,即Car car = car1
会调用car的拷贝构造函数,生成一个新的临时对象,这个临时对象的创建完全没有必要;
- 在定义函数形参时,为了避免调用拷贝构造函数,生成新的临时对象,
定义形参使用对象的引用
,就能避免,如下所示:
对象的引用形参
void test(Car &car){
}
匿名对象
- 匿名对象:没有变量名,没有被指针指向的对象,用完之后马上析构销毁;
#include <iostream>
using namespace::std;
class Person {
public:
Person(){
cout << "Person()" << endl;
}
Person(const Person &person){
cout << "Person(const Person &person)" << endl;
}
~Person(){
cout << "~Person()" << endl;
}
void display(){
cout << "display()" << endl;
}
};
void test1(Person person){
}
int main(int argc, const char * argv[]) {
Person person;
//匿名对象
Person().display();
test1(Person());
return 0;
}
隐式构造
- C++存在隐式构造的现象,某些情况下,会隐式的调用单参数的构造函数;
#include <iostream>
using namespace::std;
class Person {
int m_age;
public:
Person() : Person(0){
cout << "Person()" << endl;
}
Person(int age = 0) : m_age(age){
cout << "Person(int age = 0)" << endl;
}
Person(const Person &person){
cout << "Person(const Person &person)" << endl;
}
~Person(){
cout << "~Person()" << endl;
}
void display(){
cout << "display() age = " << this->m_age << endl;
}
};
void test1(Person person){
}
Person test2(){
return 30;
}
#include <iostream>
using namespace::std;
class Person {
int m_age;
public:
Person() : Person(0){
cout << "Person()" << endl;
}
explicit Person(int age = 0) : m_age(age){
cout << "Person(int age = 0)" << endl;
}
Person(const Person &person){
cout << "Person(const Person &person)" << endl;
}
~Person(){
cout << "~Person()" << endl;
}
void display(){
cout << "display() age = " << this->m_age << endl;
}
};
编译器自动生成的构造函数
- C++编译器在某些特定的环境下,会默认生成无参的构造函数;
#include <iostream>
using namespace::std;
class Person {
public:
int m_age;
Person(int age = 0) : m_age(age){
cout << "Person() " << endl;
}
};
int main(int argc, const char * argv[]) {
Person person;//没有生成构造函数
person.m_age = 10;
return 0;
}
-
Person person;
没有生成构造函数,实例化对象没有调用函数;
- 以下环境会生成无参构造函数:
- 成员变量在声明的同时进行了初始化;
- 有定义虚函数;
- 虚继承其他类;
- 包含了对象类型的成员,且对象成员有构造函数;
- 父类有构造函数;