五、深拷贝与浅拷贝
浅拷贝:简单的拷贝赋值操作
深拷贝:在堆区重新开辟空间,进行拷贝操作
下面举例解释:
创建person类与测试函数
class person
{
public:person(){cout << "person默认构造函数调用" << endl;}person(int age, int height){m_age = age;m_height = new int(height);cout << "person有参构造函数调用" << endl;}~person(){cout << "person析构函数调用" << endl;}int m_age;int* m_height;
};void test01()
{person p1(22,179);cout << "年龄为" << p1.m_age << "身高为" << *p1.m_height << endl;person p2(p1);cout << "年龄为" << p2.m_age << "身高为" << *p2.m_height << endl;
}
此时p1/p2的各个属性都是相同的
而此时,由于属性中m_height是我们new出的堆区的数据,需要我们手动释放,因此我们在析构函数中写出释放的部分
~person(){if (m_height != NULL){delete m_height;m_height = NULL;}cout << "person析构函数调用" << endl;}
再次运行
程序直接崩了
因为编译器自己的拷贝构造函数是浅拷贝操作
浅拷贝带来的问题就是堆区内存重复释放
因此,使用深拷贝解决,我们自己创建一块堆区保存数据,即自己实现拷贝构造函数
person(const person& p){m_age = p.m_age;//m_height = p.m_height; //编译器默认实现,即浅拷贝m_height = new int(*p.m_height);cout << "person拷贝构造函数调用" << endl;}
成功实现
总结:如果属性有在堆区开辟的,自己要提供拷贝构造函数,防止浅拷贝带来问题
六、初始化列表
作用:使用初始化列表语法来初始化属性
语法:
构造函数() : 属性1(值1),属性2(值2),属性3(值3)...
{;
}
例:
一般情况下,我们初始化是创建对象同时赋值
class person
{
public:person(int a, int b, int c){m_a = a;m_b = b;m_c = c;}int m_a;int m_b;int m_c;
};
void test01()
{person p(10, 20, 30);cout << p.m_a << endl;cout << p.m_b << endl;cout << p.m_c << endl;
}
接下来使用初始化列表的方式初始化值
class person
{
public:person() :m_a(30), m_b(20), m_c(10){;}int m_a;int m_b;int m_c;
};
void test01()
{person p;cout << p.m_a << endl;cout << p.m_b << endl;cout << p.m_c << endl;
}
这样就实现了列表初始化值,不过这样初始化的值是固定的,可以进一步优化
七、类对象作为类成员
C++中某个类的成员可以是另一个类的对象,称该成员为对象成员
例
创建2个类:person类与car类,在person中定义2个属性,1个是人名,1个是车名,而车名来自于car类
class car // 车类
{
public:car(string car){c_car = car;cout << "car的执行" << endl;}string c_car;
};
class person // 人类
{
public:person(string name, string c_car) :m_name(name), m_car(c_car){cout << "person的执行" << endl;}string m_name;car m_car;
};
void test01()
{person p("Joyce", "BMW");cout << p.m_name << "开着" << p.m_car.c_car << endl;
}
int main()
{test01();return 0;
}
而两个类,哪个先执行,哪个先销毁?
分别在2个类中增加一句person/car的执行,即可得知哪个先执行
分别在2个类中增加析构函数并输出内容,即可得知哪个先销毁
class car
{
public:car(string car){c_car = car;cout << "car的执行" << endl;}~car(){cout << "car析构的执行" << endl;}string c_car;
};
class person
{
public:person(string name, string c_car) :m_name(name), m_car(c_car){cout << "person的执行" << endl;}~person(){cout << "person析构的执行" << endl;}string m_name;car m_car;
};
总结:
①先调用对象成员的构造,再调用本类的构造
②析构刚好相反
八、静态成员
含义:即在成员变量和成员函数前加上关键字static,称为静态成员
分类:
静态成员变量 | 所有对象共享同一份数据 |
在编译阶段分配内存 | |
类内声明,类外初始化 | |
静态成员函数 | 所有对象共享同一个函数 |
静态成员函数只能访问静态成员变量 |
例①:静态成员变量
class person
{
public:static int m_age;
};
void test01()
{person p;cout << p.m_age << endl;
}
int main()
{test01();return 0;
}
简单创建p,尝试运行p中的静态成员变量,会直接报错,因为编译不过去
// ①通过对象进行访问person p;cout << p.m_age << endl;// ②通过类名进行访问cout << person::m_age << endl;
这时需要加上类外初始化
类外随便加一句
int person::m_age = 52;
同时,静态成员变量有2种访问方式
①通过对象进行访问
②通过类名进行访问
// ①通过对象进行访问person p;cout << p.m_age << endl;// ②通过类名进行访问cout << person::m_age << endl;
而对象访问这里我们再创建一个变量,即可验证静态成员变量共享同一块数据
void test02()
{// ①通过对象进行访问person p;cout << p.m_age << endl;person p2;p2.m_age = 66;cout << p.m_age << endl;
}
2次都输出p.m_age
同时,静态成员变量也有访问权限
class person
{
private:int m_a;
};
int person::m_a = 22;
尝试cout输出
例②:静态成员函数
整体与静态成员变量规则相同,只不过在函数前加上static
class person
{
public:static void func(){m_a = 55;cout << "func的调用" << endl;}static int m_a;
};
int person::m_a = 50;
void test01()
{// 通过对象访问person p;p.func();// 通过类名访问person::func();
}
int main()
{test01();return 0;
}
不过,首先静态成员函数只能访问静态成员变量
class person
{
public:static void func() // 静态成员函数{m_a = 55;m_b = 10;cout << "func的调用" << endl;}static int m_a; // 静态成员变量int m_b; // 普通成员变量
};
直接报错
同时,静态成员函数也有访问权限
class person
{
private: // 私有权限static void func2() // 静态成员函数{cout << "func2的调用" << endl;}
};
不可访问