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

简介: C++——this指针和析构函数

文章目录

this指针和常成员函数

this指针

代码示例

常成员函数(常函数)

代码示例

析构函数(Destructor)

代码示例

拷贝构造和拷贝赋值

代码示例


this指针和常成员函数

this指针

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

代码示例

  • 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;
}


  • 执行结果
  • 20200212212949595.png
  • 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;
}


  • 执行结果
  • 20200212213323171.png

* 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;
}


  • 执行结果

20200212214047185.png

常成员函数(常函数)

  • 在一个成员函数参数表后面加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;
}


  • 执行结果

20200212214334456.png

  • 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;
}


  • 执行结果

20200212214658438.png

  • 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;
}


  • 执行结果

20200212214901304.png

析构函数(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;
}


  • 执行结果

20200212215612205.png

  • 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;
}


  • 执行结果

20200212221938738.png

拷贝构造和拷贝赋值

浅拷贝和深拷贝


  • 如果一个类中包含指针形式的成员变量,缺省的拷贝构造函数只是复制指针变量本身,而没用复制该指针所指向的内容,这种拷贝方式称为浅拷贝。
  • 浅拷贝将导致不同对象之间的数据共享,如果数据在堆区,析构时还可能发送“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;
}


  • 执行结果

20200212222340201.png

  • 实现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;
}


  • 执行结果

20200212222846733.png

  • 拷贝赋值,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;
}


  • 执行结果

20200213092952910.png

相关文章
|
7天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
34 4
|
9天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
31 4
|
24天前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
1月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
38 1
|
1月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
29 2
|
1月前
|
算法 C++
【算法】双指针+二分(C/C++
【算法】双指针+二分(C/C++
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
22 0
|
2月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
3月前
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)