C++学习笔记_04抽象类、多态 2021-04-15

简介: C++学习笔记_04抽象类、多态 2021-04-15
//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;
}

20210415180023519.png


//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;
}
相关文章
|
4月前
|
C++
c++学习笔记07 结构体
C++结构体的详细学习笔记07,涵盖了结构体的定义、使用、数组、指针、嵌套、与函数的交互以及在结构体中使用const的示例和解释。
42 0
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++】多态
多态是面向对象编程中的重要特性,允许通过基类引用调用派生类的具体方法,实现代码的灵活性和扩展性。其核心机制包括虚函数、动态绑定及继承。通过声明虚函数并让派生类重写这些函数,可以在运行时决定具体调用哪个版本的方法。此外,多态还涉及虚函数表(vtable)的使用,其中存储了虚函数的指针,确保调用正确的实现。为了防止资源泄露,基类的析构函数应声明为虚函数。多态的底层实现涉及对象内部的虚函数表指针,指向特定于类的虚函数表,支持动态方法解析。
32 1
|
2月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
47 2
C++入门12——详解多态1
|
2月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
89 1
|
3月前
|
安全 C语言 C++
C++学习笔记
C++学习笔记
|
4月前
|
C++
【学习笔记】【C/C++】 c++字面值常量
【学习笔记】【C/C++】 c++字面值常量
46 1
|
4月前
|
编译器 C++
【C/C++学习笔记】C++声明与定义以及头文件与源文件的用途
【C/C++学习笔记】C++声明与定义以及头文件与源文件的用途
63 0
|
4月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
55 0
|
4月前
|
C++
c++学习笔记09 引用
C++引用的详细学习笔记,解释了引用的概念、语法、使用注意事项以及引用与变量的关系。
45 0
|
29天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
50 2