C++学习笔记_04抽象类、多态 2021-04-15
2023-09-27 14:25:47 时间
//C++学习笔记_04抽象类、多态 (多重继承的歧义性问题 和 virtual虚继承)
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
class CBase
{
public:
//我们的类,一定至少要有一个构造函数
//如果不定义构造函数,系统会自动生成一个空的构造函数(构造函数内不做任何事情)
//同样的,如果不定义析构函数,系统会自动生成一个空的析构函数(构造函数内不做任何事情)
//CBase();
//如果我们没有定义无参的构造函数(默认构造函数),我们定义了其他构造函数,则不会再生成默认构造函数来
CBase(int x);
~CBase();
void Print();
};
/*
CBase::CBase(){cout << "构造 CBase..." << endl;}
*/
CBase::CBase(int x) {cout << "构造 CBase..." << x << endl;}
CBase::~CBase() {cout << "析构 CBase..." << endl;}
void CBase::Print() {cout << "I am CBase..." << endl;}
class CSuper
{
public:
CSuper();
~CSuper();
void Print();
};
CSuper::CSuper() {cout << "构造 CSuper..." << endl;}
CSuper::~CSuper() {cout << "析构 CSuper..." << endl;}
void CSuper::Print() {cout << "I am CSuper..." << endl;}
//我们的子类,在构造的时候,会自动调用父类的默认构造函数
//如果父类,没有定义构造函数-->不会有问题,因为会自动生成一个
//如果父类,定义了构造函数,但不是默认构造函数 (定义的这个构造函数带参数)
// --》子类构造的时候会出错 (找不到父类的默认构造函数)
//解决方式:
// 1:给父类定义默认构造函数(无参的)
// 2:构造子类的时候,显示的指定调用那个父类的构造函数,加上 :CBase(1)
class Derived :public CBase, public CSuper
{
public:
Derived();
~Derived();
void Print();
};
Derived::Derived() :CBase(1) {cout << "构造 Derived..." << endl;}
Derived::~Derived() {cout << "析构 Derived..." << endl;}
void Derived::Print() {cout << "I am Derived..." << endl;}
void TestBase()
{
//CBase B1(1);
Derived D1;
//D1 调用Print() 函数,两个父类都定义了,无法确认调用哪一个
//--》显示指定调用哪个, 使用 CBase:: 指定函数的作用域
//D1.Print();
D1.CBase::Print();
D1.CSuper::Print();
//当然,如果我们自己定义了 Print,那么就可以直接调用了
D1.Print();
}
class AAA:virtual public CSuper
{
public:
AAA();
};
AAA::AAA(){cout << "构造AAA" << endl;}
class BBB :virtual public CSuper
{
public:
BBB();
};
BBB::BBB(){cout << "构造BBB" << endl;}
class CCC :public AAA, public BBB
{
public:
CCC();
};
CCC::CCC(){cout << "构造CCC" << endl;}
void TestDiamondInherit()
{
//构造 C1 先构造 AAA, 然后构造 BBB, 最后构造 CCC
// 在构造 AAA的时候,又需要先构造AAA的父类 CSuper, 然后才构造BBB
// 在构造 BBB的时候,又需要先构造BBB的父类 CSuper, 然后才构造BBB
//--》构造顺序:CSuper --> AAA --> CSuper -->BBB --> CCC
CCC C1;
//==》 上面有个问题:CSuper 会被重复构造,冗余
//--》解决方式:中间层 AAA 和 BBB 继承 CSuper的时候,定义为虚继承
//class AAA :virtual public CSuper
//class BBB :virtual public CSuper
//虚继承后,CSuper 被 AAA 和BBB共用,被构造一次后,不会重复构造
}
int main()
{
//TestBase();
TestDiamondInherit();
//system("pause");
return 0;
}
//C++学习笔记_04抽象类、多态 (菱形继承)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
class CPerson
{
private:
char name[16];
protected: //对象不能直接访问,但是允许子类中的成员函数进行访问
unsigned int age;
public:
CPerson();
CPerson(char szName[], unsigned int uiAge);
~CPerson();
void Print();
};
CPerson::CPerson(){}
CPerson::CPerson(char szName[], unsigned int uiAge){
//cout << "构造 CPerson..." << endl;
strcpy(name,szName);
age = uiAge;
}
CPerson::~CPerson(){}
void CPerson::Print(){
cout << "姓名 :" << name << endl;
cout << "年龄 :" << age << endl;
}
//CWorker 继承 CPerson: 也就是说,不需要重写 某些函数或者变量
//无论什么继承方式,父类的 private 成员,子类不允许访问
//对于父类的 public 成员,和 protected成员:
//public 表示公有继承方式:父类的成员,访问权限不变 (不降权)
//private 表示私有继承: 父类的成员,相当于子类的 私有成员 (降权)
//protected 表示保护继承: 父类的成员,相当于子类的 protected成员(降权)
class CWorker:public CPerson
{
private:
char company[20]; //公司
public:
CWorker(char szName[], unsigned int uiAge, char szCompany[]);
~CWorker();
void Carry();
void Print();
unsigned int GetAge(){//不允许调用 父类 的私有成员
return age; //如果我们想调用的话,可以把 age 声明成 protected 成员
}
};
CWorker::CWorker(char szName[], unsigned int uiAge, char szCompany[]) :CPerson(szName, uiAge){
//CPerson::CPerson(szName, uiAge); //不能这样写,因为这样写相当于定义一个新 CPerson 对象
//在外部调用父类构造函数构造对象,
//如果在函数里面 调用, 则会生成一个新的 CPerson 对象 (匿名对象)
strcpy(company,szCompany);
}
CWorker::~CWorker(){}
void CWorker::Carry(){ cout << "Carry something ..." << endl; };
void CWorker::Print(){
CPerson::Print(); //指定调用父类的Print函数,
//不加CPerson:: 则会有限调用自己定义的 Print() --> 无限递归
cout << "公司 :" << company << endl;
}
class CFarmer:public CPerson
{
private:
char address[20]; //农民的土地位置
public:
CFarmer(char szName[], unsigned int uiAge, char szAddr[]);
~CFarmer(){}
void Sow();
void Print();
};
CFarmer::CFarmer(char szName[], unsigned int uiAge, char szAddr[]) :CPerson(szName, uiAge){
strcpy(address,szAddr);
}
void CFarmer:: Print(){
CPerson::Print();
cout << "土地 :" << address << endl;
}
void CFarmer::Sow(){ cout << "Sow seed ..." << endl; };
class CMigrantWorker :public CWorker, public CFarmer
{
private:
char cardStr[20]; //暂住证卡号
public:
CMigrantWorker(char szName[], unsigned int uiAge, char szAddr[], char szCompany[], char szCard[]);
~CMigrantWorker();
void Print();
};
CMigrantWorker::CMigrantWorker(char szName[], unsigned int uiAge, char szAddr[], char szCompany[], char szCard[])
:CWorker(szName, uiAge, szCompany), CFarmer(szName, uiAge, szAddr){
strcpy(cardStr,szCard);
}
CMigrantWorker::~CMigrantWorker(){}
void CMigrantWorker::Print(){
CWorker::Print();
CFarmer::Print();
cout << "暂住证:" << cardStr << endl;
}
void TestInherit()
{
CWorker W1("小王", 30, "腾信");
W1.Print(); //事实上就调用了父类的 Print()
//并不是说父类CPerson 的所有函数,CWorker 都能调用
//CWorker 不能调用 CPerson 的 private 中的变量和函数
//W1.age = 40;// 不允许调用父类的私有成员
//protected:受保护的成员,子类中的函数,可以直接调用。
//如果子类也定义了 Print() 函数(同名,而且入参一样),那么,会隐藏掉父类的函数。
CMigrantWorker M1("张铁柱", 40, "空空搬家", "麦田1号", "zzz20170322");
M1.Print();
M1.Sow();
M1.Carry();
}
int main()
{
TestInherit();
//system("pause");
return 0;
}
//C++学习笔记_04抽象类、多态 (继承\抽象类\多态)
#include <cstdio>
#include <cstring>
#include<iostream>
using namespace std;
class CAnimal
{
protected:
char name[16];
public:
CAnimal(char szName[]) {strcpy(name,szName);}
void eat() {cout << "Animal " << name << " 吃东西 ..." << endl;}
virtual void play() {cout << "Animal " << name << " 瞎玩..." << endl;}
virtual void run()=0;//直接用0给函数赋值 run()函数就 成了纯虚函数
//也是用于实现多态,不过纯虚函数要求子类必须实现这个函数
//一个类中,只要存在纯虚函数,那么这个类我们称之为抽象类
//抽象类不能生成对象
/* {cout << "Animal " << name << " 瞎跑..." << endl;}*/
~CAnimal(){}
};
//CMonkey 继承了 CAnimal
//那么 CAnimal内的所有成员,在 CMonkey里面都包含了
//既然包含的话,需要初始化(使用构造函数初始化)
class CMonkey :public CAnimal
{
private:
public:
//构造子类的之前,会先构造父类(不写则默认调用父类的默认构造函数)
//父类没有默认构造函数,显式指定构造方式为 CAnimal(szName)
CMonkey(char szName[]) :CAnimal(szName) {}
~CMonkey() {};
void eat() {cout << "Monkey " << name << " 吃香蕉 ..." << endl;}
void play() {cout << "Monkey " << name << " 翻跟头... " << endl;}
void run() {cout << "Monkey " << name << " 手脚并用的跑..." << endl;}
};
class CBird :public CAnimal
{
public:
CBird(char szName[]) :CAnimal(szName){}
~CBird(){}
void eat() {cout << "Bird " << name << " 吃虫子 ..." << endl;}
void play() {cout << "Bird " << name << " 唱歌... " << endl;}
void run() {cout << "Bird " << name << " 飞翔..." << endl;}
};
void TestAnimal()
{
//动物园开春晚。各种动物报名。
//我们要使用一个数组来保存各个动物
CMonkey M1("小猴子");
CMonkey M2("老猴子");
CBird B1("叽叽");
CBird B2("喳喳");
CAnimal *pAni[4]; //指针数组:数组里面的元素都是指针
//定义父类的指针,指向子类的对象
pAni[0] = &M1;
pAni[1] = &M2;
pAni[2] = &B1;
pAni[3] = &B2;
for (int i = 0; i < 4; i++){
//一旦把他们综合到一起了,他们就失去了自己独有的特性
//都会调用父类的函数
pAni[i]->eat();
}
cout<<endl;
for (int i = 0; i < 4; i++){
//现在我们把 父类 的成员函数 play 前面加一个 virtual 来修饰
//表示play 是一个虚函数
//在调用的时候,优先调用子类的函数
pAni[i]->play();
//通过指针数组来 调用 方法:
//会根据 指向对象的不同,来调用 不同的 方法
//---不同的对象,打开姿势不一样 ---》多态
}
cout<<endl;
CAnimal &Ani = M1; //Ani 是 CAnimal 变量
//如果说,它是一个别名的话, Ani 和 M1 应该是完全一样的
//但是我们调用函数来看看
Ani.eat();
M1.eat();
//他们调用同一个方法,动作是不一样的,所以不能说两个是一样的,并不是取一个别名
//应该说,引用定义了一个隐含指针ptr,指向 M1, 我们使用 Ani 的时候,就相当于使用 *ptr
cout<<endl;
//我们不能使用对象来做
//CAnimal arrAni[4] = { M1, M2, B1, B2 };//抽象类不能生成对象
//这里事实上,调用的是 复制构造函数(只复制数据) arrAni[i] 就是 CAnimal对象
for (int i = 0; i < 4; i++) {
//把 run 定义成虚函数后
//如果子类没有实现 (CBird) --> 会调用父类的函数
//我们有可能漏了这个东西?
//动物跑动,这个方法不叫笼统,我们需要各个具体动物自己定义跑动
//比如猴子-跑,虫子-爬,乌-飞..
//强制子类必须定义run()方法-->使用纯虚函数
pAni[i]->run();
}
}
int main()
{
TestAnimal();
//system("pause");
return 0;
}
相关文章
- 为什么C++永不过时?
- 神经网络与机器学习 笔记—Rosenblatt感知器收敛算法C++实现
- 《C++ Primer Plus》学习笔记2
- 势头强劲的 Python PK 强大的 C++,究竟谁更胜一筹?
- C++中局部变量可以和全局变量重名吗?
- 《C++面向对象高效编程(第2版)》——2.14 类的实现者
- C++ 动态链接库 DLL 的一些笔记
- ROS Noetic入门笔记(六)使用C++编写简单的Publisher与Suscriber
- C++11 智能指针 知识整理笔记
- C++牛客网剑指Offer学习笔记(二)
- 《Effective C++》学习笔记条款13 以对象管理资源
- 【C++快速上手】十一、assert学习笔记
- 【C++快速上手】三、static学习笔记
- C++ Primer笔记13_运算符重载_总结
- C++MFC编程笔记day01 MFC介绍、创建MFC程序和重写消息处理
- C++ Primer 学习笔记_104_特殊工具与技术 --嵌套类