zoukankan      html  css  js  c++  java
  • C++课程学习笔记第三周:类和对象提高

    前言:本文主要是根据MOOC网北大课程——《程序设计与算法(三):C++面向对象程序设计》内容整理归纳而来,整理的课程大纲详见 https://www.cnblogs.com/inchbyinch/p/12398921.html

    本文进一步介绍了类和对象的知识,包括this关键字、静态成员、成员对象、友元和常量成员函数。

    1 this指针

    this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。

    • 其作用就是指向成员函数所作用的对象。
    • 注意在静态成员函数中不能使用this,因为静态成员函数本质是全局函数,不作用于任何对象。
    • 成员函数在编译时,会被偷偷塞进一个this指针,但静态成员函数不会。

    this本质:

    • this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。
    • this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。
    • 在《C++函数编译原理和成员函数的实现》一节中讲到,成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this,它是成员函数和成员变量关联的桥梁。
    //示例1
    class Complex {
        public:
            double real, imag;
            void Print() { cout << real << "," << imag ; }
            Complex(double r,double i):real(r),imag(i){ }
            Complex AddOne() {
                this->real ++; //等价于 real ++;
                this->Print(); //等价于 Print
                return * this;
            }
    };
    
    //示例2:很诡异的一个示例,看起来是错的,但理解了this的本质就知道实际是对的。
    class A{
        int i;
        public:
            void Hello() { cout << "hello" << endl; }
    };
    int main(){
        A * p = NULL;
        p->Hello();
    } // 输出:hello  注意:如果在Hello()函数中使用了成员变量i,则会报错
    

    2 静态成员变量和静态成员函数

    • 在定义前面加了static关键字的成员,即为静态成员。
    • 普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。故sizeof运算符sizeof(CMyclass)不会计算静态成员变量。
    • 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
    • 静态成员不需要通过对象就能访问。
    • 静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。静态成员函数本质上是全局函数。设置静态成员这种机制的目的,是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。

    访问方法:

    • 类名::成员名,比如 CRectangle::PrintTotal();
    • 对象名.成员名,比如 CRectangle r; r.PrintTotal();
    • 指针->成员名,比如 CRectangle * p = &r; p->PrintTotal();
    • 引用.成员名,比如 CRectangle & ref = r; int n = ref.nTotalNumber;

    注意:

    • 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
    • 必须在定义类的文件中对静态成员变量进行一次说明或初始化。否则编译能通过,链接不能通过。
    • 若静态成员变量定义为private,不能在外部调用。(见示例)
    • 静态成员变量和函数在类的内部声明时用static关键字,后续初始化或实现时不再加static。
    //示例1。此例中应再自定义复制构造函数,否则逻辑疏漏。
    class CRectangle{
        private:
            int w, h;
            static int nTotalArea; // 静态成员变量
            static int nTotalNumber;
        public:
            CRectangle(int w_,int h_);
            ~CRectangle();
            static void PrintTotal(); // 静态成员函数
    };
    CRectangle::CRectangle(int w_,int h_){
        w = w_;
        h = h_;
        nTotalNumber ++;
        nTotalArea += w * h;
    }
    CRectangle::~CRectangle(){
        nTotalNumber --;
        nTotalArea -= w * h;
    }
    void CRectangle::PrintTotal(){
        cout << nTotalNumber << "," << nTotalArea << endl;
    }
    
    int CRectangle::nTotalNumber = 0;
    int CRectangle::nTotalArea = 0;
    //必须在定义类的文件中对静态成员变量进行一次说明或初始化。否则编译能通过,链接不能通过。
    int main(){
        CRectangle r1(3,3), r2(2,2);
        //cout << CRectangle::nTotalNumber; // Wrong ,  私有
        CRectangle::PrintTotal();
        r1.PrintTotal();
        return 0;
    }
    

    3 成员对象和封闭类

    定义和使用:

    • 有成员对象的类叫 封闭(enclosing)类。
    • 通过封闭类的构造函数的初始化列表,来初始化封闭类和成员变量及成员对象。

    构造函数和析构函数执行顺序:

    • 封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数。
    • 对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关。
    • 当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。
    • 封闭类的对象,如果是用默认复制构造函数初始化的,那么它里面包含的成员对象,也会用复制构造函数初始化。
    class CCar { // 汽车类
        private:
            int price; // 价格
            CTyre tyre;
            CEngine engine;
        public:
            CCar(int p,int tr,int tw );
    };
    CCar::CCar(int p,int tr,int w):price(p),tyre(tr, w){};
    
    int main(){
        CCar car(20000,17,225);
        return 0;
    }
    

    4 友元

    • 友元分为友元函数和友元类两种,用friend声明一下即可。
    • 可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元。
    • 友元函数:一个类的友元函数可以访问该类的私有成员。友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员。
    • 友元类之间的关系不能颠倒,不能传递,不能继承。
    class CCar{
        private:
            int price;
            friend int MostExpensiveCar( CCar cars[], int total); //声明友元函数
            friend void CDriver::ModifyCar(CCar * pCar); //声明友元函数
            friend class CDriver; //声明CDriver为友元类
    };
    class CDriver{
        public:
            CCar myCar;
            void ModifyCar() { // 改装汽车
                //因CDriver 是CCar 的友元类,故此处可以访问其私有成员
                myCar.price += 1000;
             }
    };
    int main(){ return 0; }
    

    5 常量成员函数

    • 常量对象:如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字。
    • 常量成员函数:在类的成员函数后面加const关键字(包括声明和定义)。
    • 常量成员函数内部不能改变属性的值(mutable成员变量除外),也不能调用非常量成员函数(因为这些函数可能会改变值)。
    • 两个函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
    //示例1
    class Sample {
        private :
            int value;
        public:
            Sample() { }
            void SetValue() {  }
    };
    const Sample Obj; //常量对象
    Obj.SetValue(); //错误。常量对象只能使用构造函数、析构函数和带const说明的函数
    
    
    //示例2
    class CTest {
        private :
            int n;
        public:
            CTest() { n = 1 ; }
            int GetValue() const { return n ; }
            int GetValue() { return 2 * n ; }
    };
    int main() {
        const CTest objTest1;
        CTest objTest2;
        cout << objTest1.GetValue() << "," << objTest2.GetValue(); //1,2
        return 0;
    }
    
  • 相关阅读:
    open-falcon实现邮件报警
    open-falcon监控Flume
    Ubuntu下安装open-falcon-v0.2.1
    Python学习笔记——发邮件
    Flume的监控参数
    Ubuntu下安装Kafka Manager
    Ubuntu系统监控indicator-sysmonitor
    kafka性能测试
    Ubuntu下安装sbt
    Ubuntu安装shadow$ocks-libev
  • 原文地址:https://www.cnblogs.com/inchbyinch/p/12185207.html
Copyright © 2011-2022 走看看