目录
1.类的访问权限public、protected、private
一、类
1.类的访问权限public、protected、private
①公共权限public
成员在类内可以访问,类外可以访问,友元可以访问,继承可以访问
②保护权限protected
成员在类内可以访问,类外不能访问,友元可以访问,继承可以访问
③私有权限private
成员在类内可以访问,类外不能访问,友元可以访问,继承无法访问
④补充
struct默认权限是public,class默认权限是private
2.构造函数和析构函数
1.构造函数
主要作用在于创建对象时为对象的成员属性赋值,构造函数有编译器自动调用,无需手动调用。
① 构造函数语法
- 函数名:类名(){ }
- 构造函数没有返回值,也不写void;
- 函数名称与类名相同;
- 构造函数可以有参数,因此可以发生重载;
- 程序在调用对象前会自动调用构造函数,无需手动调用,而且只会调用一次。
② 构造函数分类与调用
分类:无参构造、有参构造、拷贝构造
调用:括号法(调用无参构造时不能加括号)、显示法、隐式转换法
构造函数调用规则:
默认情况下,c++编译器至少给一个类添加3个函数:构造函数(空实现)、析构函数(空实现)、拷贝构造函数(对属性进行值拷贝)
ps:如果自己写了拷贝构造函数,编译器将不再提供其他构造函数,需要自己写,否则会语法报错
③ 拷贝构造使用场景
1.使用一个已经创建完毕的对象来初始化一个新对象;
2.值传递的方式给函数参数传值
3.值方式返回局部对象
④深拷贝与浅拷贝(面试常问)
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
2.析构函数
主要作用在于对象销毁前系统自动调用,执行一些清理工作。
析构函数语法
- 函数名:类名(){ }
- 析构函数没有返回值,也不写void;
- 函数名称与类名相同,在名称前加上符号~;
- 析构函数不可以有参数,因此不可以发生重载;
- 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次。
3.初始化列表
- 构造函数():属性1(值1)属性2(值2)属性3(值3)...{ }
- 构造函数(int a , int b,...)属性1(值1)属性2(值2)...{ }
三、继承
语法:class 子类:继承方式 父类
1.继承方式
- 公共继承
- 保护继承
- 私有继承
2.继承同名成员处理方式
同名成员属性:
- 直接访问,访问的是子类中的成员
- 想要访问父类的成员,需要加上作用域(ps:s.Base::m_A)
同名成员函数:
- 直接调用,调用的是子类中的成员
- 想要调用父类的成员,需要加上作用域(ps:s.Base::func())
ps:如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数,如果想访问父类中被隐藏的同名成员函数,需要加作用域。
静态成员对象除了上述方式,还能直接通过类名来访问:
- Son::m_A
- Son::Base::m_A
- Son::func()
- Son::Base::func()
四、多态
1.多态的分类
- 静态多态:函数重载和运算符重载,复用函数名
- 动态多态:派生类和虚函数实现运行时多态
2.静动态多态的区别
- 静态多态的函数地址早绑定-编译阶段确定函数地址
- 动态多态的函数地址晚绑定-运行阶段确定函数地址
3.动态多态满足条件
- 1.有继承关系
- 2.子类重写父类的虚函数(重写代表函数返回值类型、函数名、参数列表完全相同)
动态多态的使用:父类的指针或者引用,执行子类对象
4.多态的优点
- 代码组织结构清晰
- 可读性强
- 利于前后期拓展及维护
举例用普通方式和多态方式分别实现计算器,观察区别。
普通写法:
class Calculator
{
public:
int m_A;
int m_B;
int getResult(string oper)
{
if (oper == "+")
return m_A + m_B;
else if (oper == "-")
return m_A - m_B;
else if (oper == "*")
return m_A * m_B;
else if (oper == "/")
return m_A / m_B;
}
};
void test01()
{
Calculator c;
c.m_A = 10;
c.m_B = 10;
cout << c.m_A << " + " << c.m_B << " = " << c.getResult("+") << endl;
cout << c.m_A << " - " << c.m_B << " = " << c.getResult("-") << endl;
cout << c.m_A << " * " << c.m_B << " = " << c.getResult("*") << endl;
cout << c.m_A << " / " << c.m_B << " = " << c.getResult("/") << endl;
}
维护时定位问题较难,修改需要改源码。
多态写法:
class AbstractCalcultor
{
public:
virtual int getResult()
{
return 0;
}
int m_A;
int m_B;
};
class AddCalcultor :public AbstractCalcultor
{
public:
int getResult()
{
return m_A + m_B;
}
};
class SubCalcultor :public AbstractCalcultor
{
public:
int getResult()
{
return m_A - m_B;
}
};
class Mulalcultor :public AbstractCalcultor
{
public:
int getResult()
{
return m_A * m_B;
}
};
class DivCalcultor :public AbstractCalcultor
{
public:
int getResult()
{
return m_A / m_B;
}
};
void test02()
{
AbstractCalcultor *cal = new AddCalcultor;
cal->m_A = 100;
cal->m_B = 100;
cout << cal->m_A << " + " << cal->m_B << " = " << cal->getResult() << endl;
delete cal;
cal = new SubCalcultor;
cal->m_A = 100;
cal->m_B = 100;
cout << cal->m_A << " - " << cal->m_B << " = " << cal->getResult() << endl;
delete cal;
cal = new Mulalcultor;
cal->m_A = 100;
cal->m_B = 100;
cout << cal->m_A << " * " << cal->m_B << " = " << cal->getResult() << endl;
delete cal;
cal = new DivCalcultor;
cal->m_A = 100;
cal->m_B = 100;
cout << cal->m_A << " / " << cal->m_B << " = " << cal->getResult() << endl;
delete cal;
}
代码量虽然增加了,但结构清晰,利于维护与修改。
5.纯虚函数和抽象类
在多态中,父类中虚函数的实现通常无意义,主要都是调用子类中重写的部分,因此可以将虚函数改为纯虚函数。
纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0
当类中有了纯虚函数,就成了抽象类。
抽象类的特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则子类也属于抽象类
6.虚析构与纯虚析构
①共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
②区别:
纯虚析构该类属于抽象类
③语法:
虚析构:virtual ~类名(){ }
纯虚析构: virtual ~类名() = 0 (类内写声明)
类名::类名(){ } (类外写实现)