面向对象设计思想
一个需要做的东西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中不能传入实际的参数