> 文章列表 > 对象的初始化与清理部分_2

对象的初始化与清理部分_2

对象的初始化与清理部分_2

五、深拷贝与浅拷贝

浅拷贝:简单的拷贝赋值操作

深拷贝:在堆区重新开辟空间,进行拷贝操作

下面举例解释:

创建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;}
};

不可访问