其实这也不算是day06吧,梳理了好几天,算是总结为一个专题,作为C++类的重点要点,主要参考资料是《C++新经典》!
基础知识点
认识类
类是自定义的数据类型,包括成员(属性)与方法(行为);
类内成员与方法具有访问权限,默认private,另有public和protected可供设置;
文件防卫
使用一组预编译命令,防止重复编译
#ifndef __XXXXX__
#define __XXXX__
.....
#endif
访问权限
Public:没有限制;
Protected:类内成员函数中,子类成员函数中,类友元函数;
Private:类内成员函数中,类友元函数;
构造函数
基础知识点
- 说明:构造函数名与类名相同,在创建对象的时候由系统自动调用,可用来进行成员初始化;
- 特点:无返回值;不可手工调用;一般public(单例模式是例外);
- 可重载:意味着一个类中可以有多个构造函数,差别在于参数列表;
- 无参构造函数就是默认构造函数;
- 存在继承关系,先执行父类构造函数,再执行子类构造函数;
重要语法
- 隐式转换:定义类的对象时,写作:classname test={参数列表};实际进行过隐式转换,为了防止隐式转换,可以在对应构造函数前加上关键字explicit;
- 初始化列表:冒号逗号式写法,写在构造函数参数列表后,真正的初始化({}中已经是赋值)在函数体执行之前就完成了,执行顺序与成员定义顺序相同;
- 类内初始值:C++11标准允许为类中成员赋初始值:int hours{5};
- const成员与自定义类型成员都要通过初始化列表进行初始化;
类中重点
inline 内联函数声明,标记在类外(直接在类中定义自动成为内联函数),但这仅仅是一种建议,具体成功与否取决于编译器;
mutable 可修改,与const正好相反。由于const函数不允许修改成员变量的值,但是有时候必须要修改某个成员变量,可以将该成员用mutable修饰:mutable int hours;
this指针 指向对象本身,有资料显示这个this是类成员函数隐含的第一个参数,会被传递到成员函数中,用以访问非static的成员;
static 静态成员与函数,由类独自享有,不属于对象;静态成员函数也一般只能操作静态成员,因为没有this指针不能访问一般成员;静态成员声明在类中:static int hours; 并在cpp中进行初始化int Time::hours=5;
default 简便指定默认构造函数:Time()=default;//则不用再进行函数实现,自动生成默认构造函数;
delete 显式禁用某个函数:Time()=delete;不让系统生成或使用默认构造函数;
拷贝构造函数
基础知识点
默认拷贝构造函数:无自定义时系统合成,类对象的成员逐个复制(赋值);
概念:第一个参数为所属类类型引用,若有其他参数必带默认值的构造函数为拷贝构造函数;
Time(const Time& time,int a=1);
什么时候调用拷贝构造函数?
- 同一语句里既有创建又有赋值就会调用拷贝构造函数:
- 类对象作为实参对形参进行值传递;
- 类对象作为返回值;
隐式转换:单参数构造函数往往使用explicit防止;拷贝构造函数往往不使用explicit;
深拷贝与浅拷贝
针对指针:浅拷贝后两个指针指向同一个内存空间,深拷贝需要不但对指针进行拷贝,并对指针指向的内容进行拷贝,经过深拷贝后的指针是指向两个不同地址的指针。
析构函数
系统具有自动合成的析构函数(程序员不处理);
作用:用于释放内存等善后处理(如构造函数中有new的操作,使用析构进行delete防止内存泄漏),这种销毁并非在析构函数内,而是函数执行完成后由系统统一处理的销毁,且先定义先初始化,后销毁,这个顺序是反的。
调用:类对象离开作用域,自动调用;
重载:析构函数无返回值,无参数列表,不能重载;
虚函数:析构函数推荐设置为虚函数,保证父指针new的子对象(开辟的是子对象的空间)能调用到子对象的虚构函数(动态绑定);virtual ~Time();父类析构函数的virtual会继承给子类;
运算符重载
一般形式:returntype operator运算符(参数表)
bool operator==(Time& t);
bool Time::operator==(Time& t){
if(Hours==t.Hours)//this->Hours==t.Hours
return true;
return false;
}
类的赋值运算符重载:会有默认的赋值运算符重载(=),但是要实现更为复杂的功能时候要进行自定义,这是区别于拷贝构造函数的(也就是使用了赋值但没有创建不调用拷贝构造的情况)。
public://必定是public的,使用private就是禁用的意思了
Time& operator=(const Time&);
Time& Time::operator=(const Time& t){
//赋值逻辑
return *this;
}
要注意返回值类型(类型引用),参数列表(数据保护),this指针;
函数遮蔽
无论返回值与参数,只要父类与子类具有同名函数,子类函数就会遮蔽父类同名函数(一遮多):只要子类有一个和父类中同名的函数,子类调用就不可能调用到父类的同名函数(即使参数不同),如果仍要调用:子类对象名.父类名::函数名();进行调用;或者使用using函数允许父类同名函数与子类同名函数(但参数不同)构成重载:
public:
using 父类名::函数名;//此时就可以将父类同名函数拉取到子类,完全相同的遮蔽,参数不同的构成重载;
多态
父子指针
父类指针可以new一个子类对象:父类* ptr=new 子类();//子类可以看做具有冗余信息,完全能满足父类成员数据填充需求;是用子信息填充父信息,用法是父指针,数据是子类;
子类指针不能new一个父类对象:子类* ptr=new 父类();//错误,存在缺失信息;
虚函数
一个函数一旦生命为virtual,子类不管是否标记都是虚函数;为了避免写错,一般在结尾加上override关键字;
子类虚函数与父类虚函数函数名与形参都要一样,这是与屏蔽对立的做法,一般称为重写;
运行时绑定(父指针与子对象),注意用法是父指针,数据(函数实现)是子类的含义;
总结:
父指针调用虚成员函数,动态绑定到给定对象的虚函数上;这就体现了同一个主体面对不同消息时的不同响应,这就是多态;
执行虚函数来自于真实绑定的对象;
纯虚函数与抽象类
纯虚函数在父类中声明:virtual void func()=0;任何子类都要定义该虚函数的实现,否则还是纯虚函数(成为抽象类);
带纯虚函数就是抽象类,抽象类不能实例化对象;
友元
友元函数
类内 声明为友元函数friend void func();,则友元函数内可让类对象访问私有成员(不是成员函数不能绕过对象直接访问私有);
友元关系是单向的,保证数据安全;
友元类
class A{
friend class B;//B的成员函数范围内可让A对象访问私有成员等
}
友元成员函数
仅仅是让B类部分成员函数成为A类友元函数;
友元:提高了数据访问的灵活性,但是破坏了数据的封装;