C++——this指针和析构函数


this指针和常成员函数

this指针

  1. 类中的构造函数和成员函数都隐藏了一个该类类型的指针参数,名为this;在构造函数或者成员访问类中的其它成员时,本质都是通过this指针实现的。
    对于构造函数,this指针指向正在创建的对象;
    对于成员函数,this指针指向该函数的调用对象。
  2. 显示使用this指针的场景
    • 区分作用域;
    • 从成员函数返回调用对象自身;
    • 从类的内部销毁对象的自身;
    • 作为函数的实参,实现对象间的交互。

代码示例

  • this.cpp
#include <iostream>
using namespace std;

class User{
public:
    /*User(const string& name,int age)
        :m_name(name),m_age(age){
        cout << "构造函数:" << this << endl;
    }*/
    //使用this指针区分成员变量和参数变量
    User(const string& m_name,int m_age){
        this->m_name = m_name;
        this->m_age = m_age;
    }
    void print(void){
        cout << "我叫" << this->m_name << 
          ",今年" << this->m_age << "岁" 
          << endl;
    }/*编译器编译以后:
    void print(User* this){
        cout << this->m_name << .. 
            << this->m_age << .. 
    }*/
private:
    string m_name;
    int m_age;
};
int main(void)
{
    User u1("张三",22);
    cout << "&u1=" << &u1 << endl;
    
    User u2("李四",23);
    cout << "&u2=" << &u2 << endl;

    u1.print();//User::print(&u1);
    u2.print();//User::print(&u2);
    return 0;
}
  • 执行结果
    在这里插入图片描述* 02this.cpp
#include <iostream>
using namespace std;
class Counter{
public:
    Counter(int count=0):m_count(count){}
    Counter& add(void){
        ++m_count;
        //this指向调用对象
        //*this就是调用对象自身
        return *this;//返回自引用
    }
    void destroy(void){
        //...
        cout << "this=" << this << endl;
        delete this;//对象自销毁
    }
    int m_count;
};
int main(void)
{
    Counter cn;
    cn.add().add().add();
    cout << cn.m_count << endl;//3

    Counter* pcn = new Counter;
    //...
    cout << "pcn=" << pcn << endl;
    //delete pcn;
    pcn->destroy();

    return 0;
}
  • 执行结果
    在这里插入图片描述* 03this.cpp
#include <iostream>
using namespace std;

class Student;//短视声明
class Teacher{
public:
    //向参数表示的学生对象提问
    void educate(Student* s);
    //获取学生对象回答的答案
    void reply(const string& answer);
private:
    string m_answer;//保存答案
};
class Student{
public:
    //接收问题,并将答案传回给教师对象
    void ask(
        const string& question,Teacher* t);
};
void Teacher::educate(Student* s){
    s->ask("什么是this指针?",this);
    cout << "学生回答:" << m_answer << endl;
}
void Teacher::reply(const string& answer){
    m_answer = answer;
}
void Student::ask(
    const string& question,Teacher* t){
    cout << "问题:" << question << endl;
    t->reply("this指针指向调用对象的地址");
}
int main(void)
{
    Teacher teacher;
    Student student;
    teacher.educate(&student);
    return 0;
}
  • 执行结果
    在这里插入图片描述

常成员函数(常函数)

  • 在一个成员函数参数表后面加const修饰,表示这个函数是常成员函数。
    返回类型 函数名(形参表) const{函数体}
  • 常函数中的this指针是一个常指针,不能再常函数中修改成员变量的值。(注:被mutable关键字修饰的成员变量,可以在常函数中被修改)
  • 非常对象既可以调用常函数也可以调用非常函数,但是常对象只能调用常函数,不能调用非常函数。(注:常对象也包括常指针或常引用)
  • 函数名和形参相同的成员函数,其常版本和非常版本可以构成重载关系,常对象调用常数版本,非常对象调用非常版本。

代码示例

  • constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    A(int data = 0):m_data(data){}
    void print(void) const {//常函数
        cout << m_data++ << endl;
    }/*
    void print(const A* this){
        cout << this->m_data++ << endl;
    }*/
private:
    mutable int m_data;
};
int main(void)
{
    A a(100);
    a.print();//100
    a.print();//101
    a.print();//102
    return 0;
}
  • 执行结果
    在这里插入图片描述
  • 02constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    //void func1(const A* this)
    void func1(void) const {
        cout << "常函数" << endl;
    }
    //void func2(A* this)
    void func2(void){
        cout << "非 常函数" << endl;
    }
};
int main(void)
{
    A a;
    a.func1();//A::func1(&a),A*
    a.func2();

    const A a2 = a;
    a2.func1();//A::func1(&a2),const A*
    //a2.func2();//error
    
    const A* pa = &a;//pa常指针
    pa->func1();
    //pa->func2();//error
   
    const A& ra = a;//ra常引用
    ra.func1();
    //ra.func2();//error

    return 0;
}
  • 执行结果
    在这里插入图片描述
  • 03constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
    void func(void)const{//const A* this
        cout << "func常版本" << endl;
    }
    void func(void){//A* this
        cout << "func非常版本" << endl;
    }
};
int main(void)
{
    A a;
    a.func();//非常版本
    const A& ra = a;
    ra.func();//常版本
    return 0;
}
  • 执行结果
    在这里插入图片描述

析构函数(Destructor)

  • 语法
  class 类名{
  		~类名(void){
  			负责清理对象生命期内动态分配的资源
  		}
  };
  1)函数名必须是 “~类名”
  2)没有返回类型,也没有参数
  3)不能被重载,一个类只能有一个析构函数
  • 当对象被销毁时,该对象的析构函数自动被调用和执行;
    • 1)栈对象离开作用域时,其析构函数被作用域终止右花括号“}”调用;
    • 2)堆对象的析构函数被delete操作符调用。
  • 如果一个类没用显示定义析构函数,那么编译器会为这个类提供一个缺省的析构函数;
    • 1)对基类类型的成员变量,什么也不做;
    • 2)对类 类型成员变量(成员子对象),会自动调用相应类的析构函数。
  • 对象的创建和销毁过程
    • 1)对象创建
      –》分配内存
      –》构造成员子对象(按声明顺序)
      –》执行构造函数代码
    • 2)对象销毁
      –》执行析构函数代码
      –》析构成员子对象(按声明逆序)
      –》释放内存

代码示例

  • destructor.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析构函数" << endl;
        delete m_data;
        m_data = NULL;
    }
private:
    int* m_data;
};
int main(void)
{
    if(1){
        Integer i(100);
        i.print();//100
        cout << "test1" << endl;
        Integer* pi = new Integer(200);
        pi->print();//200
        delete pi;//delete-->调用析构
        cout << "test3" << endl;
    }//}-->调用析构函数
    cout << "test2" << endl;
    return 0;
}
  • 执行结果
    在这里插入图片描述

  • 02destructor.cpp

#include <iostream>
using namespace std;
class A{
public:
    A(void){
        cout << "A::A(void)" << endl;
    }
    ~A(void){
        cout << "A::~A(void)" << endl;
    }
};
class B{
public:
    B(void){
        cout << "B::B(void)" << endl;
    }
    ~B(void){
        cout << "B::~B(void)" << endl;
    }
    A m_a;//成员子对象
};
int main(void)
{
    B b;
    return 0;
}
  • 执行结果
    在这里插入图片描述

拷贝构造和拷贝赋值

浅拷贝和深拷贝

  • 如果一个类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制指针变量本身,而没用复制该指针所指向的内容,这种拷贝方式称为浅拷贝。
  • 浅拷贝将导致不同对象之间的数据共享,如果数据在堆区,析构时还可能发送“double free”,导致进程的终止,这时需要定义一个支持复制指针指向内容的拷贝构造函数,即深拷贝。

拷贝赋值

  • 当两个对象进行赋值操作时,比如“i3 = i2”,编译器会将其自动翻译成 i3.operator=(i2)成员函数的调用形式,其中“operator=”称为拷贝赋值操作符函数,通过它实现两个对象的赋值操作,该函数的返回结果就是表达式结果。
  • 但是编译器缺省提供的拷贝赋值函数和缺省的拷贝构造函数类似,也就是浅拷贝,只是复制了指针变量本身,没用复制指针所指向的内容,有“double free”和内存泄漏的风险。
  • 为了得到深拷贝的效果,避免错误,必须自己定义一个支持深拷贝的拷贝赋值函数。
类名& operator=(const 类名& that){
		if(&that != this){//防止自赋值
			释放旧内存;
			分配新内存;
			拷贝新数据;
		}
		return *this;//返回自引用
	}
	this指向调用对象(左操作数)
	that对应参数对象(右操作数)

代码示例

  • copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析构函数" << endl;
        delete m_data;
        m_data = NULL;
    }
    /*编译器提供的缺省拷贝构造函数(浅拷贝)*/
    /*Integer(const Integer& that){
        cout << "缺省的拷贝构造" << endl;
        m_data = that.m_data;
    }*/
    /*自定义深拷贝构造函数*/
    Integer(const Integer& that){
        cout << "自定义深拷贝" << endl;
        //m_data = new int;
        //*m_data = *that.m_data;
        m_data = new int(*that.m_data);
    }
private:
    int* m_data;
};
int main(void)
{
    Integer i1(100);
    Integer i2 = i1;//拷贝构造
    i1.print();//100
    i2.print();//100
    return 0;
}
  • 执行结果
    在这里插入图片描述
  • 实现string类的基本,string.cpp
#include <iostream>
#include <cstring>
using namespace std;
class String{
public:
    //构造函数
    String(const char* str){
        m_str = new char[strlen(str)+1];
        strcpy(m_str,str);
    }
    //练习:析构函数,拷贝构造
    ~String(void){
        delete[] m_str;
        m_str = NULL;
    }
    String(const String& that){
        m_str = 
            new char[strlen(that.m_str)+1];
        strcpy(m_str,that.m_str);
    }
    const char* c_str(void)const{
        return m_str;
    }
private:
    char* m_str;
};
int main(void)
{
    String s("hello");
    cout << s.c_str() << endl;//hello
    String s2 = s;
    cout << s2.c_str() << endl;//hello

    return 0;
}
  • 执行结果
    在这里插入图片描述
  • 拷贝赋值,copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
    Integer(int data = 0)
        :m_data(new int(data)){
        //m_data = new int(data);
    }
    void print(void)const{
        cout << *m_data << endl;
    }
    ~Integer(void){
        cout << "Integer的析构函数" << endl;
        delete m_data;
        m_data = NULL;
    }
    /*编译器提供的缺省拷贝构造函数(浅拷贝)*/
    /*Integer(const Integer& that){
        cout << "缺省的拷贝构造" << endl;
        m_data = that.m_data;
    }*/
    /*自定义深拷贝构造函数*/
    Integer(const Integer& that){
        cout << "自定义深拷贝" << endl;
        //m_data = new int;
        //*m_data = *that.m_data;
        m_data = new int(*that.m_data);
    }
    /*编译器缺省实现的拷贝赋值操作符函数*/
    //i3 = i2;//i3.operator=(i2)
    /*
    Integer& operator=(const Integer& that){
        cout << "缺省拷贝赋值函数" << endl;
        m_data = that.m_data;
        return *this;
    }*/
    //自定义深拷贝赋值操作符函数
    Integer& operator=(const Integer& that){
        cout << "自定义深拷贝赋值" << endl;
        if(&that != this){
            delete m_data;
            m_data = new int;
            *m_data = *that.m_data;
        }
        return *this;
    }
private:
    int* m_data;
};
int main(void)
{
    Integer i1(100);
    Integer i2(i1);//拷贝构造
    i1.print();//100
    i2.print();//100
    
    Integer i3;

    //i3.operator=(i2);
    i3 = i2;//拷贝赋值
    i3.print();//100
    return 0;
}
  • 执行结果
    在这里插入图片描述
发布了102 篇原创文章 · 获赞 27 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_37596943/article/details/104286577