4.2.5 深拷贝与浅拷贝
浅拷贝:编译器提供的简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
示例:
class Person {
public://无参(默认)构造函数Person() {cout << "无参构造函数!" << endl;}//有参构造函数Person(int age ,int height) {cout << "有参构造函数!" << endl;m_age = age;m_height = new int(height); //利用关键字new把身高放在堆区,返回的是一个地址}//拷贝构造函数 Person(const Person& p) {cout << "拷贝构造函数!" << endl;//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题//自己写一个拷贝构造函数,解决浅拷贝带来的问题:在堆区再开辟一段空间m_age = p.m_age;//m_height = p.m_height (编译器自动提供时的拷贝构造函数写法)m_height = new int(*p.m_height); //深拷贝重新开辟一块内存//通过传入的地址进行解引用之后,再在堆区申请一块内存存入}//析构函数~Person() {
//析构代码,将堆区开辟的数据做释放操作(堆区的数据需要程序员手动开辟,也需要程序员手动释放)
//在对象销毁前对堆区的数据释放掉(test01执行完了之后),所以在析构函数时把数据释放干净cout << "析构函数!" << endl;if (m_height != NULL) //如果该指针不为空,就将其用delete删除{delete m_height;m_height = NULL; //防止野指针出现,将其置空}}
public:int m_age;int* m_height; //用指针是为了把数据开辟到堆区
};void test01()
{Person p1(18, 180);Person p2(p1); //当我们不提供拷贝构造函数数,编译器自动帮我们提供,并且做浅拷贝cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}int main() {test01();system("pause");return 0;
}
具体差别如下图所示:
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
4.2.6 初始化列表
作用:
C++提供了初始化列表语法,为类中的属性进行初始化(类似于构造函数的初始化)
语法:构造函数():属性1(值1),属性2(值2)... {}
示例:
class Person {
public:传统方式初始化,创建对象同时赋值//Person(int a, int b, int c) {// m_A = a;// m_B = b;// m_C = c;//}//初始化列表方式初始化Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}// 写法2:Person() :m_A(1), m_B(2), m_C(3) {} 但是值被固定,不够灵活void PrintPerson() {cout << "mA:" << m_A << endl;cout << "mB:" << m_B << endl;cout << "mC:" << m_C << endl;}
private:int m_A;int m_B;int m_C;
};int main() {Person p(1, 2, 3);//对应类中的写法2:Person p; p.PrintPerson();system("pause");return 0;
}
4.2.7 类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
构造函数:先构造类中的成员(调用类中成员的构造),再构造本类
析构函数:与构造顺序相反
例如:
class A {}
class B
{A a;
}
示例:
class Phone
{
public:Phone(string name){m_PhoneName = name;cout << "Phone构造" << endl;}~Phone(){cout << "Phone析构" << endl;}string m_PhoneName;};class Person
{
public://初始化列表可以告诉编译器调用哪一个构造函数//相当于 Phone m_Phone = pName 隐式转换法用pName创建对象Person(string name, string pName) :m_Name(name), m_Phone(pName){ cout << "Person构造" << endl;}~Person(){cout << "Person析构" << endl;}string m_Name;Phone m_Phone;};
void test01()
{//当类中成员是其他类对象时,我们称该成员为 对象成员//构造的顺序是 :先调用对象成员的构造,再调用本类构造//析构顺序与构造相反Person p("张三" , "华为mate60 Pro");cout << p.m_Name << " 使用" << p.m_Phone.m_PhoneName << " 手机! " << endl;}int main() {test01();system("pause");return 0;
}
4.2.8 静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员;包括静态成员变量和静态成员函数。
静态成员变量 | 静态成员函数 |
特点 | |
(在内存中只有一个值,其他函数修改之后再调用也会变成修改后的值)
|
|
调用方式(静态成员函数的调用要增加作用域) | |
|
示例1 :静态成员变量
class Person
{public:static int m_A; //静态成员变量(类内声明)private:static int m_B; //静态成员变量也是有访问权限的
};
int Person::m_A = 10; //类外初始化(为了说明是Person下的要写作用域Person::)
int Person::m_B = 10;void test01()
{//静态成员变量两种访问方式//1、通过对象Person p1;p1.m_A = 100;cout << "p1.m_A = " << p1.m_A << endl;Person p2;p2.m_A = 200; //用p2去修改m_A的值cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据cout << "p2.m_A = " << p2.m_A << endl;//2、通过类名cout << "m_A = " << Person::m_A << endl;//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}int main() {test01();system("pause");return 0;
}
示例2:静态成员函数
class Person
{public:static void func() //静态成员函数{cout << "func调用" << endl;m_A = 100; //只能访问静态成员变量//m_B = 100; //错误,不可以访问非静态成员变量//非静态成员变量必须通过创建对象才能够调用,当调用静态成员函数(程序中只有一份值)不知道改变的是哪一个对象上面的非静态成员变量(无法区分)}static int m_A; //静态成员变量(类内声明)int m_B; // 非静态成员变量
private://静态成员函数也是有访问权限的static void func2(){cout << "func2调用" << endl;}
};
int Person::m_A = 10; //(类外初始化)void test01()
{//静态成员变量两种访问方式//1、通过对象Person p1;p1.func();//2、通过类名Person::func(); //不用对象直接通过类名进行调用(但是要写明作用域)//Person::func2(); //私有权限访问不到
}int main() {test01();system("pause");return 0;
}