c++构造函数:
(1)构造函数名必须与类名相同
(2)无返回值
(3)访问权限符一般设置为public
(4)无自定义构造函数,编译器提供默认构造函数,构造函数只调用一次
c++析构函数:
(1)析构函数与类名相同,前面加~符号
(2)析构函数没有参数,不能重载,一个类里只有一个析构函数
(3)析构函数无返回值
(4)无自定义析构函数,编译器提供默认析构函数
当程序结束时,编译器会自动调用析构函数完成对象的清理工作,如果类中没有定义析构函数,编译器会提供一个默认的析构函数,但默认的析构函数只能完成栈内存对象的资源清理,无法完成堆内存堆对象的清理。因此需要自定义析构函数,析构函数的调用情况由以下几种
(1)在一个函数中自定义了一个对象,当函数调用结束时,对象应当被立即释放,对象释放之前编译器会调用析构函数释放资源。
(2)对于static修饰的对象和全局对象,只有在程序结束时编译器才会自动调用析构函数。
(3)对于new运算符创建的对象,在调用delete释放时,编译器会调用析构函数释放资源。
注意:析构函数的调用顺序与构造函数的调用顺序是相反的。在构造对象和析构对象时,c++遵循先构造的后析构,后构造的先析构。(栈)
#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
string name;
int age;
public:
Person(string name);
~Person();
};
Person::Person(string name) {
this->name = name;
cout << "调用person的构造函数:"<<name << endl;
}
Person::~Person() {
cout << "调用person的析构函数:" <<name<< endl;
}
int main() {
Person person1("1");
Person person2("2");
return 0;
}
c++拷贝构造函数:
拷贝构造函数是一种特殊的构造函数,它具有构造函数的所有特性,并且使用本类对象的引用作为形参,能够通过一个已经存在的对象初始化该类的另一个对象,为了使引用的对象不被修改,通常使用const修饰引用的对象。
拷贝构造函数的调用情况
(1)使用一个对象初始化另外一个对象
(2)对象作为参数传递给函数。当函数的参数作为对象时,编译器会调用拷贝构造函数将实参传递给形参
(3)函数返回值为对象。当函数返回值为对象时,编译器会调用拷贝构造函数将返回值复制到临时对象传出
示例:(1)这是使用一个对象初始化另外一个对象,只完成简单的赋值操作,即浅拷贝
#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
string name;
int age;
public:
Person(string name, int age);
Person(const Person& person);
~Person();
};
Person::Person(string name, int age) {
this->name = name;
this->age = age;
cout << "调用person的构造函数:" << name << endl;
}
Person::Person(const Person& person) {//拷贝构造函数
name = person.name;
age = person.age;
cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
cout << "调用析构函数" << name << " " << age << endl;
}
int main() {
Person person1("张三", 18);
Person person2(person1);
return 0;
}
使用匿名对象赋值
int main() {
Person person1 = Person("张三",18);
//不要利用拷贝构造函数初始化匿名对象 如
//Person(person1);编译器认为Person(person1)相当于Person person1,而person1对象已声明
Person person2 = person1;
return 0;
}
如果程序没有定义拷贝构造函数,c++会提供一个默认的拷贝构造函数,默认拷贝构造函数只能完成简单的赋值操作,无法完成堆内存成员数据的拷贝
#include <iostream>
using namespace std;
#include <iostream>
class Person {
private:
string name;
int age;
public:
Person(string name,int age);
~Person();
};
Person::Person(string name,int age) {
this->name = name;
this->age = age;
cout << "调用person的构造函数:"<<name << endl;
}
Person::~Person() {
cout << "调用析构函数"<<name<<" "<<age << endl;
}
int main() {
Person person1("张三",18);
Person person2(person1);
return 0;
}
(2)对象作为参数传递给函数。当函数的参数作为对象时,编译器会调用拷贝构造函数将实参传递给形参
void test1(Person p) {
cout << "对象作为参数传递会调用拷贝构造函数,创建一个拷贝对象" << endl;
}
int main() {
Person person1("张三",18);
test1(person1);
return 0;
}
引用传递则不会调用拷贝构造函数
void test1(Person &p) {
cout << "对象作为引用就不会拷贝了,自然不会调用拷贝构造函数" << endl;
}
int main() {
Person person1("张三", 18);
test1(person1);
return 0;
}
(3)函数返回值为对象。当函数返回值为对象时,编译器会调用拷贝构造函数将返回值复制到临时对象传出
Person test1() {
Person person("李四",18);
cout << &person << endl;
return person;
}
int main() {
//Person person1("张三", 18);
Person person1 = test1();
cout << &person1 << endl;
return 0;
}
呃,这个应该和编译器有关,我这里并没有调用拷贝构造函数。老的版本或许有。
c++构造函数调用规则:
(1)如果自定义了有参构造函数,编译器不在提供默认构造函数,但是提供默认拷贝构造函数
#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
string name;
int age;
public:
Person(string name, int age);
};
Person::Person(string name, int age) {
this->name = name;
this->age = age;
cout << "调用构造函数:" << name << " " << age << endl;
}
int main() {
//Person person;报错
Person person1("张三", 18);
Person person2(person1);//会调用拷贝构造函数
cout << person2.name << " " << person2.age << endl;
return 0;
}
(2)如果自定义了拷贝构造函数,编译器不在提供其它默认构造函数
#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
string name;
int age;
public:
Person(const Person& person);
};
Person::Person(const Person& person) {
name = person.name;
age = person.age;
cout << "调用拷贝构造函数" << endl;
}
int main() {
//Person person;报错
return 0;
}
c++浅拷贝
如果类中有指针类型的数据,默认的拷贝构造函数只是进行简单的指针赋值,即将新对象的指针成员指向原有对象的指针指向的内存空间(即拷贝了地址),并没有为新对象的指针成员申请空间,这种情况称为浅拷贝。
注意:浅拷贝在析构指向堆内存空间的变量,往往会出现多次析构而导致程序错误。
#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
string name;
int *age;
public:
Person(string name, int age);
Person(const Person& person);
~Person();
};
Person::Person(string name, int age) {
this->name = name;
this->age = new int(age);
cout << "调用构造函数:" << endl;
}
Person::Person(const Person& person) {
name = person.name;
age = person.age;//编译器的简单赋值操作,即浅拷贝
cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
//析构代码将堆区开辟的内存数据释放
if (age != NULL) {
delete age;
age = NULL;
}
cout << "调用析构函数:" <<name<< endl;
}
int main() {
Person person1("张三", 18);
Person person2(person1);
return 0;
}
程序在此发生错误
Person::~Person() {
//析构代码将堆区开辟的内存数据释放
if (age != NULL) {
delete age;
age = NULL;
}
cout << "调用析构函数:" <<name<< endl;
}
浅拷贝
在析构person2对象时释放了age指向的堆内存空间的数据,当析构person1对象时age指向的堆内存空间已经被释放,再次释放内存空间的资源而引发了程序异常,称为重析构
c++深拷贝
c++深拷贝可以为新对象的指针分配一块内存空间,将数据复制到新的空间
#include <iostream>
using namespace std;
#include <iostream>
class Person {
public:
string name;
int* age;
public:
Person(string name, int age);
Person(const Person& person);
~Person();
};
Person::Person(string name, int age) {
this->name = name;
this->age = new int(age);
cout << "调用构造函数:" << name << endl;
}
Person::Person(const Person& person) {
name = person.name;
age = new int(*person.age);//深拷贝,为age指向新的堆区空间
cout << "调用拷贝构造函数" << endl;
}
Person::~Person() {
//析构代码将堆区开辟的内存数据释放
if (age != NULL) {
delete age;
age = NULL;
}
cout << "调用析构函数:" << name << endl;
}
int main() {
Person person1("张三", 18);
Person person2(person1);
cout << "person1.age地址" << person1.age << endl;
cout << "person2.age地址" << person2.age << endl;
cout << person2.name << " " << *person2.age << endl;
return 0;
}
深拷贝
Person::Person(const Person& person) {
name = person.name;
age = new int(*person.age);//深拷贝,为age指向新的堆区空间
cout << "调用拷贝构造函数" << endl;
}
注: 如果有堆区开辟的成员变量,需要自定义深拷贝构造函数,以防浅拷贝带来的析构各自对象资源的问题
c++含有成员对象的类的构造函数:
class B{
...
};
class A{
public:
B b;//对象b作为类A的成员变量
};
如果类A包含一个类B对象作为成员变量,如果类B构造函数有参数,其参数要从类A的构造函数中传入,且必须以“:”运算符初始化类B对象。即若有构造函数时,实例化类A对象前先实例化类B对象。
#include <iostream>
using namespace std;
#include <iostream>
class Car {
private:
string carName;
public:
Car(string carName) :carName(carName) {
cout << "Car类的构造函数:" << endl;
};
};
class Person {
public:
string name;
Car car;
public:
Person(string name,string carName):name(name),car(carName) {
cout << "Person类构造函数:" << endl;
}
};
int main() {
Person person("张三", "大众汽车");
return 0;
}