【C++编程语言】之类和对象---对象初始化和清除

1.构造函数和析构函数-----对象的创建和清除

​ 对象的初始化和清除也是非常重要的安全问题

​ 一个对象或者变量没有初始化,对其使用后果是未知

​ 同样的使用完一个对象或变量,没有及时清除,也会造成一定的安全问题。

​ C++利用了构造函数和析构函数解决上述问题,这个函数将会被编译器自动调用,完成对象初始化和清除工作。对象的初始化和清除工作是编译器强制我们做的事情,因此如果我们不提供构造和析构函数,编译器会提供编译器创造的构造函数和析构函数是空实现。

构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数有编译器自动调用,无需手动调用。

析构函数:主要作用于对像销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}

​ 1.构造函数,没有返回值也不写void

​ 2.函数名称与类名相同

​ 3.构造函数可以有参数,因此可以发生重载

​ 4.程序在调用对象时候会自动调用构造,无需手动调用,而且只调用一次。

析构函数语法 :~类名(){}

​ 1.析构函数,没有返回值也不写void

​ 2.函数名称与类名相同,在名称前面加上符号~

​ 3.析构函数不可以有参数,因此不可以发生重载

​ 4.程序在对象销毁前会自动调用函数,需手动调用,而且只调用一次。

//对象的初始化和清理
//1.构造函数,进行初始化操作
class Person{
	public:
    	Person(){
            cout <<"Person的构造函数"<<endl;
        }
    	~Person(){
             cout <<"Person的析构函数"<<endl;
        }
}
void test01(){
     Person p;
}
int main(){
     test01();
    //输出:Person的构造函数 Person的析构函数  
    /* test01函数的数据存放在栈区,内存在函数调用完释放,
    	所有对象的析构函数也调用了。
    */
     Person p;//输出:Person的构造函数
    //对象创建在主函数中,不会立即释放
}
2.构造函数的分类及调用

​ 两种分类方式:

​ 按参数分类:有参构造和无参构造(默认构造)

​ 按类型分类:普通类型和拷贝类型

​ 三种调用方式:

​ 括号法,显示法,隐式转换法

class Perosn{
    public:
    	//无参构造,普通类
    	Person(){
            cout<<"Person的无参构造"<<endl;
        }
    	//有参构造,普通类
    	Person(int a){
            int age = a;
            cout<<"Person的有参构造"<<endl;
        }
    	//拷贝构造函数
    	Person(const Person &p){
            int age = p.a;//将传去的人身上的所有属性,拷贝到我身上 
            cout<<"Person的拷贝构造"<<endl;
        }
}
void main(){
 	//1.括号法
    Person p1;//调用默认构造函数
    /*
		注意:调用默认构造函数时不要加(),如果加了Person p1(); 
			  编译器会认为是一个函数声明,不会创建对象
	*/
    Person p2(10);//调用有参构造函数
    Person p3(p1)//调用拷贝构造函数
        
    //2.显示法
     Person p4;//调用默认构造函数
     Person p5 = Person(10);//调用有参构造函数
     Person p6 = Person(p5);//调用拷贝构造函数
     //Person(10);单独拿出,是一个匿名对象。
     			//特点:当前行执行结束后,系统会立即回收掉匿名对象
    Person(p4);//是错误的,会报p4重定义
    /*
		注意:不要利用拷贝函数初始化匿名对象 
		编译器会认为Person(p4);等价于 Person p4;所以重定义p4了 
	*/
    //3.隐式转换法
    Person p7= 10;//相当于 Person p7 = Person(10); 
    Person p8= p7;//拷贝构造
}
3.拷贝构造函数调用时机

​ C++中拷贝构造函数调用时机通常有三种情况

​ 1.使用一个已经创建完毕的对象来初始化一个新对象。

​ 2.值传递的方式给函数参数传值

​ 3.以值方式返回局部对象

class Person{
    public:
    	int m_Age;
    	Person(){
            cout<<"Person的无参构造"<<endl;
        }
    	Person(int age){
            m_Age = age;
            cout<<"Person的有参构造"<<endl;
        }
    	Person(const Person &p){
            cout<<"Person的拷贝构造"<<endl;
        }
    	~Person(){
            cout<<"Person的析构构造"<<endl;
        }
}

//2.值传递的方式给函数参数传值
void doWork(Person p){
    
}

//3.以值方式返回局部对象
Person doWork2(){
    Person p;
    return p;//此时的p是一个局部对象,当次函数调用结束就会销毁,
    		 //所以要把值拷贝给p3。
}
void main(){
    //1.使用一个已经创建完毕的对象来初始化一个新对象。
    Person p1(20);
    Person p2(p1);
    cout << p2.m_Age<<emdl;//20
    
	//2.值传递的方式给函数参数传值
    Person p;
	doWork(p);//会调用拷贝构造函数,因为当把实参的值传给函数的形参时
			  //实际上是把实参的值复制给形参,也就是拷贝
    
	//3.以值方式返回局部对象
    Person p3 = doWork2();
}
4.构造函数的调用规则

​ 默认情况下,C++编译器至少给一个类添加三个函数

​ 1.默认构造函数(无参,函数体空)

​ 2.默认析构函数(无参,函数体空)

​ 3.默认拷贝构造函数,对属性进行值拷贝(值拷贝)

构造函数调用规则:

​ 1.如果用户定义有参构造函数,c++不提供默认无参构造,但会提供默认拷贝构造

​ 2.如果用户定义拷贝构造函数,c++不会提供其他普通构造函数。

5.深拷贝和浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑。

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

​ 深拷贝:在堆区重新申请空间,进行拷贝操作。

class Person{
    public:
    	int m_Age;
    	int *m_Height;
    	Person(int age){
            m_Age = age;
        }
    	Person(int age,int height){
            m_Age = age;
            m_Height = new int(height);//把数据存放到堆区  
        }
   		~Person(){
            //析构函数,将堆区开辟堆区数据做释放操作
            if(m_Height!=NULL){
                delete m_Height;
                m_Height = NULL;
            }
         	cout << "析构 "<<endl;   
        }
    	//自己实现拷贝函数,解决浅拷贝带来的问题
    	Person(const Person &p){
              m_Age = p.m_Age;
              m_Height = new int(*p.m_Height);
        }
};
void main(){
    Person p1(18,160);
    cout << "p1的年龄为:"<< p1.m_Age<<endl;
    //调用系统提供的拷贝函数,浅拷贝
    Person p2(p1);
    
    /*
	当数据写在堆区用浅拷贝会带来一种问题是,堆区的数据会重复释放以此报错
	*/
    Person p3(18,182);
    cout << p3.m_Age<< *p3.m_Height <<endl;
    //自己重写拷贝函数,把堆区的数据换地存放
    Person p4(p3);
}

浅拷贝造成错误的原因如下图所示: 当对象p2释放内存是会把堆区的内存也一起释放,但是当轮到对象p1是否内存是,堆区的内存已经释放了,以此造成错误。

在这里插入图片描述

6.初始化列表(不重要)

​ 作用:C++提供了初始化列表语法,用来初始化属性

​ 语法:构造函数():属性1(值1),属性2(值2)…{}

class Person{
    public:
    	int m_A;
    	int m_B;
    	int m_C;
    	//传统初始化操作
    	Person(int a, int b, int c){
            m_A = a;
            m_B = b;
            m_C = c;
        }
    
    	//初始化列表
    	Person():m_A(10),m_A(20),m_A(30){
            
        }
    
    	//两者结合
    	Person(int a, int b, int c):m_A(a),m_A(b),m_A(c){
            
        }
}
void main(){
    //传统初始化操作
    Person p(10,20,30);
    
    //初始化列表
    Person p;
    
    //两者结合初始化列表
    Person p(10,20,30);
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Unknown To Known

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值