参考:
https://www.runoob.com/cplusplus/cpp-tutorial.html
gpt生成的内容
自行编写的代码
继承
-
一个派生类继承了所有的基类方法,除了:
- 基类的构造函数、拷贝构造函数和析构函数
- 基类的重载运算符
- 基类的友元函数
-
C++11及以后支持了派生类继承基类的构造函数,但是基类的构造函数应该accessible,而且派生类中要写明using 基类名:基类名; 这个没有指定构造方法,会根据参数自动套用
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Base { public: Base() { num = 5; cout << "BaseConstructorCall:" << protectedMember << endl; } Base(int n) { num = n; cout << "BaseConstructorCallnum:" << protectedMember << endl; } ~Base() { cout << "BaseDestructorCall:" << protectedMember << endl; } protected: int protectedMember = 0; int num; }; class Derived1 : public Base { public: using Base::Base; // 这行声明继承父类的构造方法 void modifyMember(int newValue) { protectedMember = newValue; // 修改父类的成员 cout << "Derived1:" << num << endl; } }; class Derived2 : public Base { public: void modifyMember(int newValue) { protectedMember = newValue; // 修改同一个父类的成员 cout << "Derived2:" << num << endl; } }; int main() { // 先调用Base(int),所以d1.num=88,此时d1.protectedMember=0 Derived1 d1(88); // 先调用Base(),所以d2.num=5,此时d2.protectedMember=0 Derived2 d2; // d1.protectedMember改为10,输出88 d1.modifyMember(10); // d2.protectedMember=20,输出5 d2.modifyMember(20); // 开始销毁。此时先销毁d2再d1,因为与创建顺序相反(用stack记忆) return 0; } /* 输出: BaseConstructorCallnum:0 BaseConstructorCall:0 Derived1:88 Derived2:5 BaseDestructorCall:20 BaseDestructorCall:10 */
-
如何在派生类中初始化继承自基类的参数?
- 由于派生类没有继承基类的构造函数,所以不能调用基类的构造函数进行初始化
- 但是可以使用初始化列表,比如下面。但是要求基类中有对应的构造函数Shape(int, int)
class Rectangle: public Shape { public: Rectangle(int a,int b):Shape(a,b) {} };
- 初始化列表会在在派生类的构造函数执行之前执行
-
支持多继承,即一个派生类可以继承多个基类,举例:class Child: public B1, protected B2{};
- 如果多继承中遇到环状继承,比如A继承D,B继承D,C同时继承AB,那么创建C对象时会同时创建两个D对象,会有混淆。此时需要把A和B改成虚继承:class A:virtual public D
- 此时会先调用D(),然后按继承顺序调用构造函数,比如class C:public B, public A则先调用B(),然后调用A(),最后再调用C()。销毁时先调用C(),然后A(),B(),D(),刚好与调用构造函数的顺序相反
-
基类和派生类中有变量同名是可以的,甚至基类中protected的数据变量与派生类的private数据变量同名也ok,但是只用名字访问的话是派生类的,基类的可以用基类的函数去访问
-
构造函数调用顺序:基类 > 成员类 > 派生类
多态
在基类的成员函数前增加virtual关键字,以实现多态。如果不这样写的话,基类指针指向子类对象时,再调用函数,只会调用基类的函数,不会自动调用子类对应的函数。
使用virtual关键字声明的函数是虚函数
final关键字:
- C++11开始引入,在函数声明后加上final,这个函数就不能被派生类重写。例如void func() final {};
- 如果不希望一个类被继承,也可以在类名后加final关键字,如class ClassName final {};
抽象类
- 如果类中至少有1个函数被声明为纯虚函数(就是函数前面加上virtual关键字,后面加上=0;),那么这个类就是抽象类。
- 抽象类不能用于实例化对象,只能作为接口使用。可以实例化对象的类称为具体类。
- 继承抽象类的类可以实现纯虚函数,变成具体类;也可以不实现,继续保持为抽象类;也可以实现一部分纯虚函数,但只要有至少1个纯虚函数没有被实现,这个类就不是具体类。
实例:
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
// 提供接口框架的纯虚函数
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
重载
int Fruit::operator+(Fruit &f) {
return weight + f.weight;
}
函数重载:指在同一个作用域里,声明几个功能相似的同名函数,这些函数的区别在于形参不同。不能根据返回类型作为区分标志。
运算符重载:大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。
Box operator+(const Box&); // 类成员。省略一个参数是因为是跟自己操作的
Box operator+(const Box&, const Box&); // 非成员函数
注意:
- 很多运算符都可以重载,不能重载的有:. :: sizeof ?: #
- 运算符重载不能改变操作个数、优先级、结合性和语法结构
- 可以重载的:
- 双目算数运算符:±*/%
- 单目运算符:+(正)-(负)*, &, ++,- -
- 关系运算符:==, ! =, <, >, < =, > =
- 逻辑运算符:|| && !
- 位运算符:|, &, ~, ^, <<, >>
- 赋值运算符:= 以及位运算符号、算数运算符与=的联合
- 空间申请与释放:new delete new[] delete[]
- 其他:()函数调用, - >成员访问, ,逗号,[]下标