Day40、this指针和常函数、析构函数、对象的创建和销毁过程、拷贝构造和拷贝赋值(深拷贝、浅拷贝!)

来源:互联网 发布:侏罗纪世界4 知乎 编辑:程序博客网 时间:2024/05/22 23:39

一、            this和常函数

1、 this 指针

1)     类中的构造函数和成员函数都隐藏一个该类类型的指针参数,参数名为this。

2)     对于普通的成员函数,this指针就是指向调用该函数的对象

3)     对于构造函数,this指针指向正在被构造的对象

举例:

  1#include<iostream>

  2using namespace std;

  3class User{

  4public:

 5     User(const string&name,int age):

 6     m_name(name),m_age(age){

 7        cout<<"A::A()"<<this<<endl;

 8     }

 9     //void print(User* this){

 10    void print(void){

 11        cout<<m_name<<','<<m_age<<endl;

 12        cout<<this->m_name<<','<<this->m_age<<endl;

 13        //this指针指向调用对象

 14        cout<<"this="<<this<<endl;

 15     }

 16private:

 17    string m_name;

 18    int m_age;

 19};

 20int main(void){

 21    User user1("张飞",25);

 22    cout<<"&user1:"<<&user1<<endl;

 23    user1.print();//print(&user1)

24     return 0;

 25 }

2、 必须使用this指针的地方

1) 区分作用域

  1#include<iostream>

  2using namespace std;

  3class User{

  4public:

 5     //构造形参和成员变量一样的名字,通过this区分

 6     User(const string&name,int age){

 7         this->name=name;

 8         this->age=age;

 9     }

 10    void print(void){

 11        cout<<name<<","<<age<<endl;

 12     }

 13private:

 14    string name;

 15    int age;

 16};

 17int main(){

 18    User user("tangzihao",24);

 19    user.print();

 20    return 0;

 21 }

2)  从成员函数中返回对象自身 

1 #include<iostream>

  2using namespace std;

  3class counter{

  4public:

 5     counter(intdata=0):m_data(data){}

 6     counter&add(void/*counter* this*/){

 7         ++m_data;

 8         return *this;//返回自引用

 9     }

13     int m_data;

 14};

 15int main(){

 16    counter cn;

 17    cn.add().add().add();//add(&cn)

 18    cout<<cn.m_data<<endl;//3

21     return 0;

 22 }

 

3) 从类的内部销毁该对象自身-----对象自毁

  1#include<iostream>

  2using namespace std;

  3class counter{

  4public:

 5     counter(intdata=0):m_data(data){}

 6     counter&add(void/*counter* this*/){

 7         ++m_data;

 8         return *this;//返回自引用

 9     }

 10    void destory(void){

 11        delete this;

 12     }

 13    int m_data;

 14};

 15int main(){

 16    counter cn;

 17    cn.add().add().add();//add(&cn)

 18    cout<<cn.m_data<<endl;//3

 19    counter* pcn=new counter;

 20    pcn->destory();//destory(pcn)

 21    return 0;

 22 }

4)作为函数的实参,实现对象间数据交互(了解)

  1#include<iostream>

  2using namespace std;

  3class student;//短视声明

  4class teacher{

  5public:

 6     void educate(student*student);

 7     void reply(const string&answer){

 8         m_answer=answer;

 9     }

 10private:

 11    string m_answer;//保存答案

 12};

 13class student{

 14public:

 15    void ask(const string& question,teacher* t){

 16        cout<<"问题:"<<question<<endl;

 17        t->reply("this指针指向调用对象的地址");

 18     }

 19};

 20void teacher::educate(student* student){

 21    student->ask("什么是this指针",this);

 22    cout<<"学生回答"<<m_answer<<endl;

 23}  

 24int main(void){

 25    teacher tch;

 26    student sdt;

 27    tch.educate(&sdt);

 28    return 0;

 29 }

3、常函数

1)在一个普通的成员函数参数表后面加上const,这个成员函数称为常函数

返回类型 函数名(形参表)const {函数体}

2)常函数中的this指针是一个常指针,不能在常函数中修改成员变量的值

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(int data=0):m_data(data){}

 6     /*如果定义一个成员函数,里面不需要修改成员变量,只是访问,那么应该声明为    常函数*/

 7     void show(void)const{//constA* this

 8        cout<<m_data/*++*/<<endl;// 不能修改成员变量,如果修改会报错

 9     }

 10private:

 11    int m_data;

 12};

 13int main(void){

 14     Aa(123456);

 15    a.show();

16     a.show();

 17     return 0;

 18 }

123456

123456

3)mutable关键字:修饰的成员变量可以在常函数中修改

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(int data=0):m_data(data){}

 6     void show(void)const{//constA* this

 7        /* cout<<

 8            const_cast<A*>(this)->m_data++<<endl;*/

  9/*mutable修饰的成员变量在常函数中使用时会自动做常量转换,和上句意思一样*/

 10        cout<<m_data++<<endl;

 11     }

 12private:

 13    mutable int m_data;

 14};

 15int main(void){

 16     Aa(123456);

 17    a.show();

 18    a.show();

 19    return 0;

 20 }

tarena@tarena-virtual-machine:~/day40$./a.out

123456

123457

4) 非常对象既可以调用非常函数,也可以调用常函数,但是常对象只能调用常函数,不能调用非常函数

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     void func1(void)const{//this-->const A*

 6         cout<<"常函数"<<endl;

 7     }

 8     void func2(void){

 9         cout<<"非常函数"<<endl;

 10     }

 11};

 12int main(void){

 13     Aa;

 14    a.func1();//&a-->A

 15    a.func2();

 16    const A a2=a;

 17    //a2是个常对象,常对象只能调用常函数,不能调用非常函数

 18    a2.func1();//&a2-->const A*

 19    //a2.func2();error  只读调用可读可写,扩大范围,不行

 20    const A* pa=&a;//pa常指针

 21    pa->func1();

 22    //pa->func2();

 23    const A& ra=a;//ra常引用

24     ra.func1();

 25    //ra.fun2();

 26    return 0;

 27 }

tarena@tarena-virtual-machine:~/day40$./a.out

常函数

非常函数

常函数

常函数

常函数

5) 函数名形参表相同的成员函数,其常版本和非常版本可以构成有效的重载关系,常对象调用常版本,非常对象调用非常版本

   1 #include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     void func(void)const{//this-->const A*

 6         cout<<"常版本"<<endl;

 7     }

 8     void func(void){//this-->A*     //可以构成重载函数

 9         cout<<"非常版本"<<endl;

 10     }

 11};

 12int main(void){

 13     Aa;

 14    a.func();//调用非常版本

 15    const A a2=a;

 16    a2.func();

 17    return 0;

 18 }

tarena@tarena-virtual-machine:~/day40$./a.out

非常版本

常版本

二、            析构函数(Destructor)

创建对象的时候调用构造函数

1、 析构函数和构造函数形式类似,是类中特殊的成员函数

class 类名{

       ~类名(void){….}

}

1)函数名必须是:~类名

2)没有返回类型,也没有参数,所以不能被重载,一个类只能有一个析构函数。

3)主要负责清理对象生命周期中产生的动态资源

(如果用销毁new的成员变量比较麻烦,万一忘记,会造成内存泄露,析构函数自动释放new出来的)

  1#include<iostream>

  2using namespace std;

  3class integer{

  4public:

 5     integer(intdata=0):m_data(new int(data)){}

 6     ~integer(void){

 7         cout<<"integer::~integer()"<<endl;

 8         delete m_data;

 9         m_data=NULL;

 10     }

 11    int get(void)const{

 12        return *m_data;

 13     }

 14private:

 15        int* m_data;

 16};

 17int main(void){

 18    integer i(100);

 19     cout<<i.get()<<endl;//100

 20    return 0;

 21 }

2、 当对象被销毁时,该对象的析构函数将自动被执行

(构造函数:当对象被创建时,该对象的构造函数将自动被调用)

1) 栈对象(局部变量)当其离开作用域时,其析构函数被作用域终止运算符“}”调用

2) 堆对象(new出来的)的析构函数被delete运算符调用

  1#include<iostream>

  2using namespace std;

  3class A{

 4     public:

 5         ~A(void){

 6            cout<<"A::A~()"<<endl;

 7         }

  8};

  9int main(void){

 10     {

 11        A a;

 12        cout<<"test1"<<endl;

 13        A* pa=new A;

 14        delete pa;// delete -->A::~A()

 15        cout<<"test3"<<endl;

 16     }// } -->A::~A()  对象的作用域结束时调用析构函数

 17    cout<<"test2"<<endl;

 18    return 0;

 19 }

test1

A::A~()

test3

A::A~()

test2

3、 如果一个类没有显式定义析构函数,那么系统会为该类提供一个缺省析构函数

1)对基本类型的成员变量,什么都不做

2)对类类型的成员变量,调用相应类型的析构函数,析构成员子对象

  1#include<iostream>

  2using namespace std;

  3class A{

  4public:

 5     A(void){

 6        cout<<"A::A()"<<endl;

 7     }

 8     ~A(void){

 9        cout<<"A::~A()"<<endl;

 10     }

 11};

 12class B{

 13public:

 14    B(void){

 15        cout<<"B::B()"<<endl;

 16     }

 17     ~B(void){

 18        cout<<"B::~B()"<<endl;

 19     }

 20     Am_a;

 21}; 

 22int main(void){

 23     Bb;

24     return 0;

 25 }

tarena@tarena-virtual-machine:~/day40$./a.out

A::A()

B::B()

B::~B()

A::~A()

4、 对象的创建和销毁过程

1) 对象的创建

-à为对象分配内存空间

-à依次调用类类型成员变量的构造函数,构造成员子对象

à执行构造函数体代码

2) 对象的销毁

-à执行析构函数的代码、

-à调用成员子对象的析构函数

-à释放对象所占的内存

(以上代码)

三、            拷贝构造和拷贝赋值

A a1;

A a2(a1); // 拷贝构造

A  a3;

a3=a1;// 拷贝赋值

1、浅拷贝和深拷贝

如果一个包含指针形式的成员变量,缺省的拷贝构造函数只是赋值指针成员变量本身(拷贝地址),而没有赋值该指针所指向的内容,这种拷贝方式称为浅拷贝

浅拷贝将导致不同对象间数据共享(多个对象指向同一块内存中的值),如果数据是在堆区,会在析构时引发“doublefree”异常,因此就必须自己定义一个支持复制指针所指向内容的拷贝构造函数,即深拷贝

  1#include<iostream>

  2using namespace std;

  3class Integer{

  4public:

 5     Integer(intdata=0):m_data(new int(data)){} //缺省构造函数

 6    /* 缺省拷贝构造函数,浅拷贝 error//(拷地址)

 7     Integer(const Integer&that):

 8         m_data(that.m_data){}*/

 9     Integer(const Integer&that):

 10        m_data(new int(*that.m_data)){}//深拷贝:(分配地址拷数据)

 11    ~Integer(void){

 12        delete m_data;

 13        m_data=NULL;

 14     }

 15    int get(void)const{

 16        return *m_data;

 17     }

 18private:

 19    int* m_data;

 20};

 21int main(void){

 22    Integer i1(100);

 23    Integer i2(i1);//拷贝构造

24    cout<<i1.get()<<endl;//100

 25    cout<<i2.get()<<endl;//100

 26    return 0;

 27 }

tarena@tarena-virtual-machine:~/day40$./a.out

100

100

2、 类的缺省拷贝赋值和缺省拷贝构造一样,都是浅拷贝,为了得到深拷贝的效果,必须自己定义一个支持深拷贝的拷贝赋值运算符函数。

1) 防止自赋值

2) 释放旧资源

3) 分配新资源

4) 赋值新内容

5) 返回自引用

拷贝构造和拷贝赋值代码:

#include<iostream>

using namespace std;

class Integer{

public:

   Integer(int data=0):m_data(new int(data)){}

   /*缺省拷贝构造函数,浅拷贝 error//(拷地址)

   Integer(const Integer& that):

       m_data(that.m_data){} */

   Integer(const Integer& that):

       m_data(new int(*that.m_data)){}//深拷贝:(分配地址拷数据)

   /*编译器会自动添加一个拷贝赋值函数*/

   /*缺省拷贝赋值一样也是浅拷贝*/

   /*Integer& operator=(const Integer& that){

       cout<<"缺省拷贝赋值运算符函数"<<endl;

       m_data=that.m_data;

       return *this;

   }*/

   /*缺省的浅拷贝会引发doublefree,所以自己定义深拷贝赋值*/

   Integer& operator=(const Integer& that){//深拷贝

       if(&that!=this){//地址比较 防止自赋值

           delete m_data;//释放旧资源

           //分配新资源

           //拷贝新数据

           m_data=new int(*that.m_data);

       }

       return *this;//返回自引用

    }

   ~Integer(void){

       delete m_data;

       m_data=NULL;

    }

   int get(void)const{

       return *m_data;

    }

private:

   int* m_data;

};

int main(void){

   Integer i1(100);

   Integer i2(i1);//拷贝构造

   cout<<i1.get()<<endl; //100

   cout<<i2.get()<<endl; //100

   Integer i3;

   i3=i2;//拷贝赋值:i3.operator=(i2);operator是左值

   cout<<i3.get()<<endl; //100

   return 0;

}

 

 

 

 

 

练习:实现String (实现库里的string类)

class String{

public:

       //构造函数

       //析构函数

       //拷贝构造

       //拷贝赋值

private:

       char*m_str;

}

代码:重要!!!手写

#include<iostream>

#include<cstring>

using namespace std;

class String{

public:

   //构造函数

   String(const char* str=""):

       m_str(strcpy(new char[strlen(str)+1],str)){}

   //析构函数

   ~String(void){

       delete[] m_str;

       m_str=NULL;

    }

   //深拷贝构造

   String(const String& that):

       m_str(strcpy(new char[strlen(that.m_str)+1],that.m_str)){}

       

   //深拷贝赋值

   String& operator=(const String& that){

       if(&that!=this){//防止自赋值

   /*    小鸟版 */

           delete[] m_str;//释放旧资源

           m_str=new char[strlen(that.m_str)+1];//分配新资源

           strcpy(m_str,that.m_str);//赋值新内容*/

           //上面方法如果new失败不好处理         

   /*大鸟版    */

   /*      char* str=newchar[strlen(that.m_str)+1];//分配新资源

           delete[] m_str; //释放旧资源

           m_str=strcpy(str,that.m_str); //赋值新内容   */  

   /*老鸟版:*/

   /*      String temp(that);

           swap(m_str,temp.m_str);    */

           }                               

           return *this;   

    }

   const char* c_str(void)const{

       return m_str;

    }

private:

   char* m_str;

};

int main(void){

   String s1("hello world!");

   cout<<s1.c_str()<<endl;//hello world!

   String s2=s1;//拷贝构造

   cout<<s2.c_str()<<endl;//hello world!

   String s3("hello C++!");

   s2=s3;//拷贝赋值

   cout<<s2.c_str()<<endl;//hello C++!

   return 0;

}

tarena@tarena-virtual-machine:~/day40$./a.out

hello world!

hello world!

hello C++!



0 0