仅作个人笔记学习交流使用
进击的C++(一)
进击的C++(二)
进击的C++(三)
进击的C++(四)
进击的C++(五)
一、继承
面向对象中的继承指类之间的父子关系
- 子类拥有父类的所有属性和行为
- 子类是一种特殊的父类,子类可以赋值和初始化父类
- 子类对象可以当作父类对象使用
- 子类可以添加父类没有的方法和属性
class Parent
{
};
class Child : public Parent
{
};
继承方式
public | private | protected |
---|---|---|
父类成员在子类中保持原有访问级别 | 父类成员在子类中变为私有成员 | 父类中的公有成员变为保护成员,其他不变 |
C++默认继承方式为private,工程项目中一般只使用public继承。
protected 继承
- 修饰的成员不能被外界直接访问
- 修饰的成员可以被子类直接访问
继承访问级别选择
二、继承中的构造和析构
子类中定义构造函数
- 必须对继承而来的成员进行初始化
- 直接通过初始化列表或者赋值的方式进行初始化
- 调用父类构造函数进行初始化
父类构造函数在子类中的调用方式
默认(隐式)调用:适用于无参构造函数和使用默认参数的构造函数
显式调用:通过初始化列表进行调用,适用于所有父类构造函数
class Parent
{
public:
Parent()
{
cout << "Parent()" << endl;
}
Parent(string s)
{
cout << "Parent(string s) : " << s << endl;
}
};
class Child : public Parent
{
public:
Child() //隐式调用
{
cout << "Child()" << endl;
}
Child(string s) : Parent(s) //显式调用
{
cout << "Child(string s) : " << s << endl;
}
};
Child c1;
Child c2("c2");
输出
Parent()
Child()
Parent(string s) : c2
Child(string s) : c2
构造与析构顺序
对象创建时构造函数的调用顺序
- 调用父类的构造函数
- 调用成员变量的构造函数
- 调用类自身的构造函数
析构函数的调用顺序与构造顺序相反
- 调用自身的析构函数
- 调用成员变量的析构函数
- 调用父类的析构函数
class Object
{
string ms;
public:
Object(string s)
{
cout << "Object(string s) : " << s << endl;
ms = s;
}
~Object()
{
cout << "~Object() : " << ms << endl;
}
};
class Parent : public Object
{
string ms;
public:
Parent() : Object("Default parent ")
{
cout << "Parent()" << endl;
ms = "Default";
}
Parent(string s) : Object(s)
{
cout << "Parent(string s) : " << s << endl;
ms = s;
}
~Parent()
{
cout << "~Parent() : " << ms << endl;
}
};
class Child : public Parent
{
Object mO1;
Object mO2;
string ms;
public:
Child() : mO1("Default m1"), mO2("Default m2")
{
cout << "Child()" << endl;
ms = "Default";
}
Child(string s) : Parent(s), mO1(s + " m1"), mO2(s + " m2")
{
cout << "Child(string s) : " << s << endl;
ms = s;
}
~Child()
{
cout << "~Child() " << ms << endl;
}
};
Child cc("cc");
输出
Object(string s) : cc
Parent(string s) : cc
Object(string s) : cc m1
Object(string s) : cc m2
Child(string s) : cc
~Child() cc
~Object() : cc m2
~Object() : cc m1
~Parent() : cc
~Object() : cc
三、父子同名覆盖与冲突
3.1 同名成员变量
- 子类可以定义父类中的同名成员
- 子类成员将隐藏父类中的同名成员
- 父类的同名成员依旧存在于子类中,可以通过作用域分辨符(::)访问父类中的同名成员
3.2 同名成员函数
- 子类可以定义父类中的同名函数(函数重写)
- 子类无法重载父类中的同名函数(作用域不同)
- 子类中的函数将隐藏父类中的同名函数
- 父类的同名函数依旧存在于子类中,可以通过作用域分辨符(::)访问父类中的同名函数
3.3 赋值兼容
子类对象可以当作父类对象使用(兼容性)
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
当父类指针(引用)指向子类对象时——
编译器只能根据指针类型判断所指向的对象,因此认为父类指针指向的是父类对象
- 子类对象退化为父类对象
- 只能访问父类中定义的成员
- 可以直接访问被子类覆盖的同名成员
3.4 多态
多态的概念
- 根据实际的对象类型判断如何调用重写函数
- 同样的调用语句在实际运行时由多种不同的表现形态
- 父类指针(引用)指向
父类对象->则调用父类中定义的函数
子类对象->则调用子类中定义的重写函数
C++对多态的支持 - 使用关键字virtual对多态进行支持
- 被virtual声明的函数被重写后具有多态性
- 被virtual声明的函数叫做虚函数
多态的意义
- 在程序运行过程中展现出动态的特性
- 函数重写必须多态实现
- 多态是面向对象组件化程序设计的基础特性
静态联编——在程序的编译期间就能确定具体的函数调用(如函数重载)
动态联编——在程序实际运行后才能确定具体的函数调用(如函数重写)