this指针

提出的意义

C++刚出来的时候并没有特定的编译器,所以我们需要把C++程序翻译成C的程序才可以编译。

在早起有这样一个映射:$Class\rightarrow Structure$,其中

  • 成员变量映射为Structure里面的变量
  • 成员函数怎么办呢?Struct并没有,所以就提出了一个这样子的想法:通过添加一个This指针来表示他是类的成员函数,举个例子:

    加入我们有个类和成员函数是

    class A{
        public:
            int price;
            void func();
    };
    
    void A::func(int para){
        price = para;
    }

    那么也许就会翻译成

    void A_func(struct A* this, int para){
        this->price = para
    }

    通过这样的方法,当年的C++就可以实现对类的支持

尽管岁月变迁,但是原则上C++的编译依旧是类似这样的,毕竟C非常接近于汇编。所以在汇编代码中,成员函数的参数永远比代码中多一个this

使用案例

来看下面这个奇妙的使用

Code

class A {
    public:
        int time,price;
        A(int a,int b):price(b){};
        void print(){
            cout << time << '\t' << price << endl;
        }
        A* get(){
            return this;
        }
};

int main() {
    A a = A(3,5);
    A* b = a.get();
    b->time = 9;
    a.print();

    return 0;
}

输出9和5哦!

一个更奇怪的例子

Code

class A {
        int i;
    public:
        void Hello(){
            cout << "Hello" << endl;
        }
};

int main() {
    A* p = NULL;
    p->Hello();

    return 0;
}

在这个奇怪的例子中,NULL对成员函数的影响仅仅是this指针为NULL,成员函数仍能正常使用,代码会输出Hello

这个看起来奇怪,但是回想上面的提出意义,用C++翻译成C来编译的这样的基础来理解,就有完全正确。

啊对了,有个奇怪的使用方法,如果我不想在代码中使用->怎么办呢?把this转换成引用就好了

A pA = *this;

使用范围

this指针只能存在于普通的成员函数中,不能出现静态(static修饰的)函数和其他一般函数中。

初始化成员变量的的变形

为了使代码能够稍微短一点,提出了如下的构造函数,

class A{
    public:
        int time,price;
        B tmp;
        A(int a,int b,B t):time(a),price(b),tmp(time,price){};
};
class B{
    public:
        int a,b;
        B(x,y){
            a = x,b = y;
        }
}

){之间的就是这种缺省的初始化写法,你可以在括号中继续完成你的构造函数。

通过这种方法可以直接修改初始化的值,看下面的代码来理解一下

Code

class Base {
  public:
    int k;
    Base(int n) : k(n) {}
};
class Big {
  public:
    int v;
    Base b;
    Big(int x) : v(x), b(x){};
};

首先说明,这段代码可以正常运行

特别的,代码的第九行使用了base b这种无参的构造方法,但是base类本身没有无参的构造方法,为什么可以运行呢?

原因是在第10行重定义了初始化,进而可以正常使用。

注意,这不会影响构造的顺序。构造的顺序一直满足先声明的先构造,按照代码中的顺序为准。

静态成员

静态成员指在说明前面加了static关键字的成员函数或成员变量,比如说

class CRectangle {
        int w,h;
        static int nTotalArea;
        static int nTotalNumber;

    public:
        CRectangle(int w_,int h_);
        ~CRectangle();
        static void PrintTotal();
};

特别的:

  • 添加了static关键字,就相当于把其变成了全局变量/函数一般,所有的类都分享这个一个。

    因为静态函数和全局函数一样,所以它无法访问类的内部信息。

  • static的变量大小不会被计算入一个类的大小中,换句话来说就是用sizeof()是看不到的。
  • static的函数也并不具体作用于某个对象,所以可以直接访问。
CRectangle::PrintTotal();

易错点:

  1. 在使用静态变量前,一定要先进行初始化赋值才可以使用,否则会出现诡异的锅。
  2. 小心复制函数带来的问题

    • 当类对象为参数时
    • 当类对象为返回值时

封闭类

定义

  • 类的里面包了另一个类,比如上面的CRectangle类
写的时候要注意不要让构造函数少了东西

封闭类函数的构造函数和析构函数的执行顺序

  1. 封闭类对象生成时,先执行所有对象成员的构造函数,再执行封闭类的构造函数
  2. 成员对象的构造函数的调用次序和对象成员在类的说明次序保持一致,与初始化列表中出现的次序无关

    一定要写初始化语句!

  3. 封闭类的对象消亡时,先执行封闭类的析构函数,再执行成员对象的析构函数

    满足:先声明的后析构

举个例子来说明一下:

Code

class A{
        int id;
    public:
        int getID(){return id;}
        A(){id = -1;};
        A(int i):id(i){cout << "构建了一个A,id= "<< id << endl;};
        ~A(){
            cout << "销毁了一个A,id= " << id << endl;
        }
        A(A& tmp){
            id = tmp.getID();
            cout << "复制了一个A,id= " << id << endl;
        }
};

class B{
        int id;
    public:
        int getID(){return id;}
        B(){id = -1;};
        B(int i):id(i){cout << "构建了一个B,id= "<< id << endl;};
        ~B(){
            cout << "销毁了一个B,id= " << id << endl;
        }
        B(A& tmp){
            id = tmp.getID();
            cout << "复制了一个B,id= " << id << endl;
        }
};

class C{
        A a;
        B b;
    public:
        C(int a_,int b_):a(a_),b(b_){
            cout << "构建了一个C,A_id= "<< a_ << " B_id= "<<b_ << endl;
        };
        ~C(){
            cout << "销毁了一个C,A_id= "<< a.getID() << " B_id= "<< b.getID() << endl;
        }

};

int main() {
    C a(1,2);
    return 0;
}

结果输出

构建了一个A,id= 1
构建了一个B,id= 2
构建了一个C,A_id= 1 B_id= 2
销毁了一个C,A_id= 1 B_id= 2
销毁了一个B,id= 2
销毁了一个A,id= 1

常量成员

定义

  • 常量对象

    在定义时加上const关键字

    const A obj;
  • 常量成员函数

    不应修改调用其作用的对象中的非常量变量/函数(除静态成员变量或函数),但是可以读取非常量变量和常量变量,调用常量函数

    定义时加到()后面,比如

    void getValue() const;

举个例子

class A {
    public:
        int v;
        A(int vv) : v(vv) {}

        const A *getPointer() const { return this; }    //此时的this是const A*
};

int main() {
    const A a(10);
    const A *p = a.getPointer();
    cout << p->v << endl;
    return 0;
}

从例子中我们就能明白这么一个道理:

一旦用const,处处加const

友元

友元分友元函数和友元类两种

友元函数

  • 定义

    一个类的友元函数可以访问该类的私有成员

    可以将任意一个普通函数或一个类的成员函数(包括构造,析构函数)说明为另一个类的友元

举个例子:

Code

class CCar;

class CDrive {
  public:
    void ModifyCar(CCar *pCar);
};

class CCar {
    private:
        int price;


    friend int MostExpensiveCar(CCar cars[], int total); //友元函数
    friend void CDrive::ModifyCar(CCar *pCar);           //友元函数
};

void CDrive::ModifyCar(CCar *pCar) { pCar->price += 1000; }

int MostExpensiveCar(CCar cars[], int total) {
    int tmpMax = -1;
    for (int i = 0; i < total; i++)
        if (cars[i].price > tmpMax) tmpMax = cars[i].price; //由于是友元,我们才可以直接访问price

    return tmpMax;
}

友元类

  • 定义

    如果A是B的友元,那么A的成员函数可以访问B的私有成员

举个例子

Code

class CCar{
    private:
        int price;
    
    friend class CDriver;
};

class CDriver{
    public:
        CCar myCar;
        void ModifyCar(){
            myCar.price += 1000;
        }
};

注意!

友元类之间的关系不能传递,不能继承

简单来说就是,A类是B类的友元,B类是C类的友元,C类不是A类的友元。

同时友元具有单向性,A在代码中声明B是友元,B没有声明。这样会导致B的可以访问A,但是A的函数无法访问B。

太真实了吧......
Last modification:March 21st, 2020 at 10:56 am
Compared with money, your comment could inspire me more.
相较于钱财,你的留言更能激发我创作的灵感