C++notes

C++知识点,不定期更新

面向对象设计思想

一个需要做的东西A,先一层一层的找出最底层需要做的东西B,然后定义一个B类(允许只有方法),再将需要做的东西上升一个层次C再定一个C类(可以继承B,或者定义一个B类的变量,这样就可以包括B类的方法等),然后再上一层定义一个D类(同样继承C,或者定义一个C)…..如此直到到达A层

类的声明和定义

class A;//声明
class A{
    ...
};//定义,若在类中有实例化其他类的对象,需要在此类定义前,加入其他类的声明  

类继承相关:默认private继承

  • 子类继承模板类的时候,记得加与该子类相同的模板参数:

    template <typename P>  
    class Child:public Father<P>{//注意父模板类也需要传入P,否则报错
        ...
    } 
    
  • 子类在调用父类的成员函数时,最好使用 父类class名::父类成员函数名(),防止子类中有同名函数而引起的歧义(若子类中存在,不加的话,默认是采用就近调用原则的)

名称空间相关

  • 名称空间一般放入的东西:类声明、定义(不含类方法的具体实现)以及一些全局性的东西

运算符重载

  • 格式:返回类型 operator 符号(A(类名) &x){具体实现}//同类型参数可以访问私有成员(同一类的成员函数可以访问同一类的私有成员)

    bool operator *(double j);//作为类成员函数时的声明  
    bool operator *(A i, doublej)
    {
        ...
    }//作为非类成员函数时的定义,非类成员函数重载运算符时,需要传递两个参数,且分别对应了运算符的左右两边  
    

友元函数:使用非成员函数,也可以直接访问类的私有成员,非类成员函数所以具体实现时不用加::

  • 友元函数不是类的成员函数!但友元函数可作为类的扩展接口

    friend void function(A &i);//在A类中声明,则function为友元函数,可以调用A类中的私有成员,外部实现时不必再加friend  
    void function (A &i)
    {
        ...
    }//外部实现  
    

类类型的自动转换和强制类型转换(两种格式,对应不同方法)

  • 自动转换利用一个参数的构造函数实现(如:int->类类型),取消自动隐式转换在构造函数前加入explicit
  • operator 类型()

    Stonewt mycat;
    mycat = 19.2;//此处自动调用了Stonewt中的构造函数,生成了一个临时对象,再赋值给mycat,若在构造函数前加explicit则会报错
    mycat = Stonewt(19.2);//以下两种用了operator 类型() 的转换函数(转换函数必须是类方法,不能指定返回类型,不能有参数),如:operator int();  
    mycat = (Stonewt)19.2;  
    

虚函数

  • 构造函数不可以为虚函数或纯虚函数,析构函数可以为虚函数或纯虚函数
  • virtual void function();//function为虚函数,需要在类中或类外对其进行实现,子类(派生类)可以重写它:void function()
  • 纯虚函数(只在类中声明了函数体,并未在类中或类外定义该函数):
    virtual void function() = 0;//function为纯虚函数
    纯虚函数相当于一接口,想要使用它就必须要派生类来具体实现其定义,这有利于编码的分工合作

浅拷贝和深拷贝

  • 浅拷贝:只对对象的数据成员进行简单复制,涉及到动态分配时,如果按浅拷贝进行复制,不做特殊处理,复制完成后,两个对象中涉及动态分配的变量,会指向同一内存区域,导致析构释放内存时出现错误
  • 深拷贝:对新产生的指针变量进行动态内存分配再赋值给新的指针变量,并拷贝整个内存区域
  • 默认复制构造函数、=运算符,所进行的都是浅拷贝
  • 浅拷贝可能引发的问题:
    1、析构时,同一个内存空间被释放两次,会出错
    2、浅拷贝后,指针指向同一内存空间,任何一方改变都会影响到另一方

拷贝/复制构造函数

  • 默认的复制构造函数进行的是浅拷贝
  • 何种情况会调用复制构造函数:
    1、程序中需要建立一个新对象,并用另一个同类的对象对它进行初始化时:A x = y;//不同于A a;a = y;后一种情况调用的是该类等号的重载函数(若存在的话,未定义的话,会存在一个默认的,同样是进行浅拷贝)
    2、当函数的参数为类对象时,传入实参,会调用复制构造函数
    3、当函数返回类型是一个类的时候

  • 若要防止调用拷贝构造函数可声明一个private类型的拷贝函数

  • 拷贝/复制构造函数:
    func(const typename & x)#注意没void,func的命名和class名相同
    func(typename & x)
    func(volatile typename & x)
    func(const volatile typename & x)

重载与重写

  • 重载:函数的重载,只要满足名字相同,参数不同就可以实现重载(类型或个数不同都可以实现重载),只有返回类型不同,其他相同,编译器会报错,无法实现重载。返回类型和参数同时改变则可以实现重载

  • 重写:需要返回类型相同,参数相同,函数名也相同才能实现重写。若只有返回类型不同,则会报错,若参数不同则会认为是另一个函数

C++引用

  • 引用可以看做实参的别名,在函数内对形参的修改其实就是对实参的修改,非指针类型
  • C++中引用不可以传常量,如果用const修饰后则可以
  • const typename &i 和 typename const &i的区别(暂时认为是一样的)

C++ Template

  • 将数据类型变为可后期指定(使得数据类型可定制化)
  • 非类型的模板形参可以当宏定义使用,但非类型形参只能是整型、指针和引用:

    template<class T, int VALUE>//VALUE为非类型模板形参  
    class B {
        private:
            T element = VALUE;
    }
    

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

  • 静态成员变量:所有对象共享同一个静态成员变量(内存共享),初始化:类型 类名::变量名 = …;
  • 静态成员函数:

单冒号

  • 类构造函数初始化列表

    class firstclass{
        private:
            int a;
            const int b;
        public:
            firstclass();
    };
    
    firstclass::firstclass():a(1), b(1){}//对变量a和const变量b进行初始化,可以对const变量进行初始化赋值。初始化顺序要和类中成员变量的声明顺序相同  
    

对于继承的类类来说,在初始化列表中也可以进行基类的初始化,初始化的顺序是先基类初始化,然后再根据该类自己的变量的声明顺序对子类成员变量进行初始化


QT:

信号和槽

  • connect一次,就会产生一个回调函数,并不会覆盖
  • connect(viewpic, SIGNAL(clicked()), this, SLOT(paintEvent(QPaintEvent )));这样写是不对的,错在:
    1、首先信号和槽函数参数类型需要一致,就类似int对int,不管是系统自带的还是自己定义的,首先参数类型都要一致。此处clicked()是无参数的,而paintEvent(QpaintEvent
    )却带有参数
    2、其次,connect中不能传入实际的参数