• C++中与类有关的注意事项(更新中~~~)


    • 关于构造函数的调用次序,见下列代码
    #include<iostream>
    using namespace std;
    class A
    {
        private:
            int x;
        public:
            A():x(0){ cout << "Construct A----" << x << endl; }
            A(int i):x(i){ cout << "Construct A----" << x << endl; }
            ~A() { cout << "Des A----" << x << endl; }
    };
    class B
    {
        private:
            int y;
        public:
            B():y(0){ cout << "Construct B----" << y << endl; }
            B(int i):y(i){ cout << "Construct B----" << y << endl; }
            ~B() { cout << "Des B----" << y << endl; }
    };
    class C
    {
        private:
            int z;
        public:
            C(int i):z(i){ cout << "Construct C----" << z << endl; }
            ~C() { cout << "Des C----" << z << endl; }
    };
    class D:public B
    {
        public:
            A a0, a4;       // l1
            B b2, b1;       // l2
            C c1, c2;       // l3                //其构造函数调用次序与这里的顺序有关
            D():c2(2), c1(1), a4(4),B(1), b2(3) { cout << "Construct D----5" << endl; }         //B(i)调用的是D的基类构造函数,它首先开始
            ~D(){ cout << "Des D----5" << endl; }
    
    };
    int main()
    {
        D d;
    }
    

    当然了,首先调用基类的构造函数是不容置疑的,不管它在哪里,记住即可,不过关于对象成员的构造函数的调用还需注意, 见 L1, L2, L3, 它们的构造函数的调用次序与它们在此的相对次序有关,如类A排在第一行,因此先调用关于它的对象,这里还应再注意一点,尽管先定义了它的对象成员,不过它不会立即调用其默认构造函数,而是去看看你有没有写相应的初始化(注意:这里是指在类里面,而不是指main函数内以及类外函数,对于类外函数应注意,在定义类的同时必须给它附上一定的值,不过这根据需要而定,如果你已经设置了无参构造函数了或者你在类内定义了一些set函数),比如调用完基类构造函数后优先调用a0的构造函数,但初始化列表中并没有它,故调用它的默认构造函数,然后调用a4的构造函数,依此类推,就不难理解编译运行后的结果了。

    关于析构函数的调用只需知道它与构造函数的调用刚好对称即可。

    针对继承,其构造函数的一般调用顺序为基类构造函数 ---> 成员对象的构造函数 ---> 它自身的构造函数(这里是指初始化列表后大括号内的内容)

    •  类的静态成员(static member)必须在类内声明,在类外初始化。
    • 类里面的任何成员变量在定义时是不能初始化的,尽管你可以编译过。
    • 类的一个对象调用了一次构造函数之后,是不允许再次调用构造函数的。
    • 如果一个类是另一个类的友元类,那么该类的友元函数将不能访问另一个类的私有成员。
    class Base
    {
        public:
            int x = 0;
        protected:
            double y = 0;
        private:
            float z = 0;
            friend class Deri;
    };
    class Deri:public Base
    {
        protected:
            int dx = 1;
        public:
            friend void f2(Base b);
            void f3(Base b){cout << b.x << " " << b.y << " " << b.z << endl; }  //在友元类内部可以访问另一个类私有成员
    };
    void f2(Base b)
    {
        cout << "======" << endl;
        cout << b.x << " " << b.y << " " << b.z<< endl; //error, y和z都无法访问
        
    }
    • 类内的函数名不可和数据成员的名称重复。

    class First1
    {
        private:
            int memi; 
            double memd; 
        public:
            int memi(){return memi;} //error
            double getmemd(){return memd;}
    
    };
    • 无论是在类外还是在类内访问私有成员时最好设置一个接口,养成一个习惯。
    #include<iostream>
    using namespace std;
    class First1
    {
        private:
            double memd; 
        public:
            double getmemd(){return memd;}
    
    };
    int main()
    {
        First1 a;
        cout << a.getmemd() << endl;
    }
    
    • 当该类的对象离开了它的域或者delete表达式应用到一个该类的对象的指针上时,析构函数会自动被调用。
    • 看一段代码:
    #include<iostream>
    using namespace std;
    class Data
    {
        private:
            int d;
        public:
            Data():d(0){}
            Data(const int dd):d(dd){}
            Data operator+(Data b)
            {
                Data c = d + b.d;
                return c;
                /*
                    int c = d + b.d;
                    return Data(c);         //true,和下面一样
                    Data c_(c);
                    return c_;      //true
                
                
                */
            }
            friend ostream &operator<<(ostream& os, const Data &b);
    
    
    };
    ostream& operator<<(ostream& os, const Data &b)
    {
        os << b.d;
        return os;
    }
    
    int main()
    {
        Data a, b(1);
        cout << a + b << endl;
        return 0;
    }
    
    •  在使用类模板对象时,必须显示地指定模板实参,否则就会报错。
    • 在写类模板时,如何需要用到另一个类模板的私有成员,注意千万不要写成普通的友元形式,那样报错报到你哭,步骤自身感觉比较繁琐,见链接https://blog.csdn.net/lezardfu/article/details/61433246,实在不行在另一个类中写相应的public接口。

    关于运算符重载需要注意以下几点:

    • 作为类成员的重载函数(以类名作为返回类型),其形参看起来比操作数少一个,因为隐式传递,限定为第一个操作数,举个例子,如下:
    T operator + (const Data<T> d) {
            return value + d.value;
        }                /*这是一个类模板内的一个成员函数,注意人家
                            在使用类对象时显示的指定模板实参了,不要忘了,另外系统隐藏了一个
                            类对象,一般两个对象中隐藏第一个*/
    • 重载>> 和 << 时一般在public处声明(声明时不要忘记它是友元函数),在类外定义,注意它是非成员函数(这其中包括普通函数,友元函数)。但也有例外,比如你写了个类模板,并且你给它写了个运算符重载<<,这时你就不能按照常规做了,要既在类内声明,又在类内定义,同时不要忘记显示指定模板实参。注意:重载<<时写成 “friend ostream& operator<<(ostream &os, const Data<T> &s)”形式,避免出现发现不出来的错。
    • 一般将算术操作符定义为非成员函数,如+, - , *, /,不过也可以将它定义成成员函数
    friend Complex operator+(Complex a, double b) {return Complex(a.r + b, a.i);   /*这里定义成友元函数比较好*/
    
    /*注意:写成这样就不对了*/
    //例如
    Complex(Complex a, Complex b){}     /*如果你想得到a和b相加后的结果,这样写是不对的,因为多了一个*/
    /*应写成这样*/
    Complex(Complex b)
    {
        Complex c;
        ... 
        return c(...);
    }
    • 一元运算符(如++,--)因为其位置不同而导致重载形式不同,如
    friend X& operator++(X& o);         /*相当于++a,自增完后直接返回引用*/
    /*或者这样*/ X& operator++();        /*相当于++a,自增完后直接返回引用*/
    friend X operator++(X& o, int );    /*相当于a++,自增完后返回一个临时的*/
    • 赋值必须返回对*this的引用(如+=, =)
    • 下标运算符 [ ] 一般作为类成员函数,中间加上const就更好了。
    • 重载类型转化操作符时应注意1.必须定义为类成员函数. 2.不能指定返回类型。 3.必须返回要转换成的类型
    class Circle
    {
        private:
            double x, y, r;
        public:
            Circle(double a, double b, double c):x(a), y(b), r(c){}
            operator int(){return int(r);}
    
    };
    •  
  • 相关阅读:
    SSH批量部署服务
    rsync配置
    你到底有没有资本
    QT4.8.5 源码编译记录
    kernel 4.4.12 移植 HUAWEI MU609 Mini PCIe Module
    AM335x 添加 HUAWEI MU609 Mini PCIe Module,并用pppd 启动相关设备
    u-boot bootz 加载kernel 流程分析
    Linux kernel 之 socket 创建过程分析
    Linux kernel 之 uart 驱动解析
    am335x 无屏实现开关机程序
  • 原文地址:https://www.cnblogs.com/KeepZ/p/11143777.html
Copyright © 2020-2023  润新知