构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
-
一个对象或者变量没有初始状态,对其使用后果是未知
-
同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法 : 类名(){}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法 : ~类名(){}
5. 析构函数,没有返回值也不写void
6. 函数名称与类名相同,在名称前加上符号 ~
7. 析构函数不可以有参数,因此不可以发生重载
8. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
#include <iostream>
#include <cstring>
using namespace std;
class Person{
public: Person(){
cout<< "constructor called"<< endl;
}
public: ~Person(){
cout<< "xigou constructor called"<< endl;
}
};
int main() {
Person p1;
4.2.2 构造函数的分类及调用
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
#include <iostream>
#include <cstring>
using namespace std;
class Person{
// 无参构造函数 (默认)
public: Person() {
cout<< "constructor called"<< endl;
}
// 有参构造
Person(int a) {
age = a;
cout<< "youcan constructor called"<< endl;
}
// 拷贝构造函数
Person(const Person& p){
age = p.age;
cout<< "copy constructor called"<< endl;
}
// 析构函数
~Person(){
cout<< "xigou constructor called"<< endl;
}
public:
int age;
};
int main() {
// Person p1; // 自动调用无参构造函数
// cout<< p1.age << endl; // 0
// 调用方式
//1. 括号法 (常用)
///注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
// Person p2(18);
// Person p3(p2);
// cout<< p2.age << endl;
// 2 显示法
// Person p3 = Person(10);
// // 调用拷贝构造
// Person p4 = Person(p3);
// 3隐式转换法
Person p5 = 10; // = Person p3 = Person(10);
Person p6 = p5; // = Person p4 = Person(p3);
}
拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include <iostream>
#include <cstring>
using namespace std;
class Person{
// 无参构造函数 (默认)
public: Person() {
cout<< "wucan constructor called"<< endl;
mAge = 0;
}
// 有参构造
Person(int age) {
mAge = age;
cout<< "youcan constructor called"<< endl;
}
// 拷贝构造函数
Person(const Person& p){
cout<< "copy constructor called"<< endl;
mAge = p.mAge;
}
// 析构函数
~Person(){
cout<< "xigou constructor called"<< endl;
}
public:
int mAge;
};
void test01(){
// 1 使用一个已经创建完毕的对象来初始化一个新对象
Person man1(100);
// Person man2(man1); // 调用拷贝构造
// Person man3 = man1; // 调用拷贝构造
// Person man4;
// man4 = man1; //man4 在之前已经创建完了, 这里赋值操作
}
void doWork(Person p1){}
void test02(){
Person p; // 无参构造
// 2 值传递的方式给函数参数传值
doWork(p);
}
Person doWork2(){
Person p1;
cout << &p1 << endl;
return p1;
}
// 3.以值方式返回局部对象
void test03(){
Person p = doWork2();
cout << &p << endl;
}
int main() {
// 1 使用一个已经创建完毕的对象来初始化一个新对象
// test01();
// 2 值传递的方式给函数参数传值
// test02();
// 3.以值方式返回局部对象
test03();
}
构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
-
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
-
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
#include <iostream>
#include <cstring>
using namespace std;
class Person{
// 无参构造函数 (默认)
public: Person() {
cout<< "wucan constructor called"<< endl;
mAge = 0;
}
// 有参构造
Person(int age) {
mAge = age;
cout<< "youcan constructor called"<< endl;
}
// 拷贝构造函数
Person(const Person& p){
cout<< "copy constructor called"<< endl;
mAge = p.mAge;
}
// 析构函数
~Person(){
cout<< "xigou constructor called"<< endl;
}
public:
int mAge;
};
void test01(){
Person p1(18);
// Person p2; 只提供有参这样不能调用了
// 如果不写拷贝构造函数,编译器会自动添加拷贝构造,并且是浅拷贝操作
Person p2(p1);
cout<< p2.mAge << endl;
//
}
void test02(){
// 如果用户提供拷贝构造, 编译器不会提供其他构造函数
Person p1; // 会报错
Person p2(p1); // 会报错
}
int main() {
// test01();
test02();
}
初始化列表
作用:
C++提供了初始化列表语法,用来初始化属性
语法: 构造函数(): 属性1(值1), 属性2(值2)…{}
#include <iostream>
#include <cstring>
using namespace std;
class Person{
// 无参构造函数 (默认)
public:
// Person(int a, int b, int c, int d) {
// m_A = a;
// m_B = b;
// m_C = c;
// m_D = d;
// }
Person(int a, int b, int c, int d):m_A(a), m_B(b), m_C(c), m_D(d){}
void print(){
cout<< m_A <<endl;
cout<< m_B <<endl;
cout<< m_C <<endl;
cout<< m_D <<endl;
}
private:
int m_A;
int m_B;
int m_C;
int m_D;
};
int main() {
Person p1(1, 2, 3, 4);
p1.print();
}
类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
class A{}
classB(){
A a;
}
B类中有对象A作为成员,A为对象成员
那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?
#include <iostream>
#include <cstring>
using namespace std;
class Phone{
public:
Phone(string name) : m_PhoneName(name) {
cout << "Phone constructor called"<<endl;
}
Phone() {}
~Phone() {
cout << "Phone xigou constructor called"<<endl;
}
public:
string m_PhoneName;
};
class Person{
public:
Person(string name, string pNmae) : m_Name(name), m_Phone(pNmae) {
cout << "Person constructor called"<<endl;
}
~Person(){
cout << "Person xigou constructor called"<<endl;
}
void playGame(){
cout<< m_Name << "use" << m_Phone.m_PhoneName<<" phone playGame"<<endl;
}
private:
string m_Name;
Phone m_Phone;
};
int main() {
//当类中成员是其他类对象时,我们称该成员为 对象成员
//构造的顺序是 :先调用对象成员的构造,再调用本类构造
//析构顺序与构造相反
Person p("Eric", "Huawei");
p.playGame();
}
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
- 静态成员变量
- 所有对象共享同一份数据(具有记忆作用)
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
#include <iostream>
#include <cstring>
using namespace std;
class Person{
public:
// 特点:
// 在编译阶段分配内存
// 类内声明 类外初始化
// 所有对象共享同一份数据
static int m_A; //静态成员变量
private:
static int m_B; //静态成员变量也是有访问权限的
};
// :: 作用域符
int Person::m_A = 10;
int Person::m_B = 10;
int main() {
// 通过类名进行访问
cout << "m_A = " << Person::m_A << endl;
// cout << "m_A = " << Person::m_B << endl; // 权限限制
// 通过对象
Person p1;
p1.m_A = 100;
cout << "p1.m_A = " << p1.m_A << endl;
Person p2;
p2.m_A = 200;
cout << "p1.m_A = " << p1.m_A << endl;
cout << "p1.m_A = " << p2.m_A << endl; // 共享一份数据
}