純虛函數和抽象類


純虛函數和抽象類

定義

注意抽象類不能創建對象,但是可以定義一個指針

 

注意抽象類不能有任何成員結構, 成員函數必須協成純虛函數,

virtual 返回值  函數名(參數列表)=0

 注意

  含有純虛函數的類被稱為抽象類。抽象類只能作為派生類的基類,不能定義對象,但可以定義指針。在派生類實現該純虛函數后,定義抽象類對象的指針,並指向或引用子類對象。

1)在定義純虛函數時,不能定義虛函數的實現部分;

2)在沒有重新定義這種純虛函數之前,是不能調用這種函數的。

  抽象類的唯一用途是為派生類提供基類,純虛函數的作用是作為派生類中的成員函數的基礎,並實現動態多態性。繼承於抽象類的派生類如果不能實現基類中所有的純虛函數,那么這個派生類也就成了抽象類。因為它繼承了基類的抽象函數,只要含有純虛函數的類就是抽象類。純虛函數已經在抽象類中定義了這個方法的聲明,其它類中只能按照這個接口去實現。

抽象類實例

計算圖形面積

#include <iostream>

using namespace std;

// 重點
// 面向抽象類編程(面向一套預先定義好的接口編程)
// 解耦合。。模塊的划分
class Figure // 抽象類
{
public:
    // 約定一個統一的界面(接口) 讓子類使用,讓子類必須去實現
    virtual void getArea() = 0; // 純虛函數
protected:
private:
};

class Circle : public Figure {
public:
    Circle(int a, int b) {
        this->a = a;
        this->b = b;
    }

    virtual void getArea() {
        cout << "圓的面積\t" << 3.14 * a * a << endl;
    }

protected:
private:
    int a;
    int b;
};

class Sanjiao : public Figure {
public:
    Sanjiao(int a, int b) {
        this->a = a;
        this->b = b;
    }

    virtual void getArea() {
        cout << "三角形的面積\t" << a * b / 2 << endl;
    }

protected:
private:
    int a;
    int b;
};

class Squre : public Figure {
public:
    Squre(int a, int b) {
        this->a = a;
        this->b = b;
    }

    virtual void getArea() {
        cout << "四邊形的面積\t" << a * b << endl;
    }

protected:
private:
    int a;
    int b;
};

void PlayObj(Figure *base) {
    base->getArea(); // 會發生多態
}

int main() {
    // Figure f1; // 抽象類不能被實例化
    Figure *base = NULL;
    Circle c1(1, 2);
    Squre sq(1, 2);
    Sanjiao s1(2, 4);


    PlayObj(&c1);
    PlayObj(&s1);
    PlayObj(&sq);

    return 0;
}
抽象類編程例子一

計算程序員工資

忘記了手動調用delete,讓其調用析構函數比較好

第二個有點錯誤, 不能說是抽象類

#include <iostream>
using namespace std;

/*
 編寫一個c++程序 計算程序員工資(programer)
        1要求:能計算出初級程序員工資(junior_programer),中級程序員(mid_programer),高級程序員(Adv_progreamer)
        2要求利用抽象類統一界面(方便程序拓展),比如新增 計算架構師architect的工資
*/

// 程序員抽象類
class programer
{
public:
    virtual void getSal() = 0; //  抽象類接口
};

// 初級程序員
class junior_programer: public programer
{
public:
    junior_programer(char *name, char *job, int sal) // 淺拷貝
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal() // 接口類實現
    {
        cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

// 中級程序員
class mid_programer: public programer
{
public:
    mid_programer(char *name, char *job, int sal) // 淺拷貝
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal() // 接口類實現
    {
        cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

// 高級程序員
class Adv_programer: public programer
{
public:
    Adv_programer(char *name, char *job, int sal) // 淺拷貝
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal() // 接口類實現
    {
        cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

// 后來增加的 架構師類
class architect: public programer
{
public:
    architect(char *name, char *job, int sal) // 淺拷貝
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal() // 接口類實現
    {
        cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

// 計算函數,簡單框架
void jisuan(programer *base)
{
    base->getSal();
}

// 引用
void jisuan(programer &base)
{
    base.getSal();
}
int main()
{
    junior_programer junior("張三", "初級", 5000);
    mid_programer    mid("李四",    "中級", 10000);
    Adv_programer    adv("王五",    "高級", 15000);

    // 系統擴展 增加代碼 架構師工資
    architect arc("康總", "架構師", 30000);


    jisuan(&junior);
    jisuan(&mid);
    jisuan(&adv);

    jisuan(&arc);

    cout << endl;


    // 引用類型
    auto &i = junior;
    auto &j = mid;
    auto &k = adv;
    auto &l = arc;

    jisuan(i);
    jisuan(j);
    jisuan(k);
    jisuan(l);

    return 0;
}
別人的的方式
//編寫一個c++程序 計算程序員工資(programer)
//1要求:能計算出初級程序員工資(junior_programer),中級程序員(mid_programer),高級程序員(Adv_progreamer)
//2要求利用抽象類統一界面(方便程序拓展),比如新增 計算架構師architect的工資

#include <iostream>
#include <cstring>

using namespace std;

// 定義程序員類, 抽象類
class Programer {
    virtual double getSal() = 0;
};

class Junior : Programer {
public:
    Junior(char *name = NULL, char *job = NULL, double sal = 7000) {

        auto len = strlen(name);
        this->name = new char[len + 1];
        strcpy(this->name, name);

        len = strlen(job);
        this->job = new char[len + 1];
        strcpy(this->job, job);
        this->sal = sal;

    }

    virtual ~Junior() {
        delete[]name;
        delete[]job;
        sal = 0;
        name = NULL;
        job = NULL;
        cout << "j" << endl;
    }

    virtual double getSal() {
        cout << this->name << " : " << this->job << ": " << this->sal << endl;
    }

public:
    char *name;
    char *job;
    double sal;
};

class Mid : public Junior {
public:
    Mid(char *name = NULL, char *job = NULL, double sal = 10000)
            : Junior(name, job, sal) {

    }

    virtual ~Mid()  // 會默認調用父類的析構函數
    {
        cout << "m" << endl;
    }
};

// 高級的
class Adv : public Junior {
public:
    Adv(char *name = NULL, char *job = NULL, double sal = 10000)
            : Junior(name, job, sal) {

    }

    virtual ~Adv()  // 會默認調用父類的析構函數
    {
        cout << "Adv" << endl;
    }
};

void print(Junior &obj) {
    obj.getSal();
}


int main() {
    Junior j("張三", "初級", 5000);
    Mid m("李四", "中級", 10000);
    Adv a("王五", "高級", 15000);
    print(j);
    print(m);
    print(a);

    return 0;
}
自己的另一種寫法

抽象類編程

動物園類

#if 0
// main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "Animal.h"
#include "Dog.h"
#include "Cat.h"
#include "Dog.cpp"
#include "Cat.cpp"
#include "Animal.cpp"


using namespace std;

int main(void)
{
    letAnimalCry(new Dog);

    letAnimalCry(new Cat);

#if 0
    Animal *dog = new Dog;
    letAnimalCry(dog);
    delete Dog;
#endif

    return 0;
}
#endif

// --  Animal.h
#if 0
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Animal
{
public:
    //純虛函數,讓子類繼承並且實現
    virtual void voice() = 0;
    Animal();
    virtual ~Animal();
};
//架構函數
//讓動物叫
void letAnimalCry(Animal *animal);
#endif
// Animal.cpp
#if 0
#include "Animal.h"

inline
Animal::Animal()
{
    cout << "animal().." << endl;
}
inline
Animal::~Animal()
{
    cout << "~Animal()..." << endl;
}
inline
void letAnimalCry(Animal *animal)
{
    animal->voice();

    // 需要手動調用delete 讓其調用析構函數
    if (animal != NULL) {
        delete animal;
    }
}
#endif

// Dog.h------------------------
#if 0
#pragma once
#include "Animal.h"
class Dog : public Animal
{
public:
    Dog();
    ~Dog();

    virtual void voice();
};
#endif

// Dog.cpp
#if 0
#include "Dog.h"

inline
Dog::Dog()
{
    cout << "Dog().." << endl;
}

inline
Dog::~Dog()
{
    cout << "~Dog().." << endl;
}
inline
void Dog::voice()
{
    cout << "狗開始哭了, 555" << endl;
}
#endif

// Cat.h
#if 0
#pragma once
#include "Animal.h"


class Cat : public Animal
{
public:
    Cat();
    ~Cat();

    virtual void voice();
};
#endif
// Cat.cpp
#if 0
#include "Cat.h"

inline
Cat::Cat()
{
    cout << "cat().." << endl;

}

inline
Cat::~Cat()
{
    cout << "~cat().." << endl;

}
inline
void Cat::voice()
{
    cout << "小貓開始哭了,66666" << endl;
}
#endif
動物園

電腦類實例:

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>


using namespace std;

//--------  抽象層---------
//抽象CPU類
class CPU {
public:
//    CPU();

    virtual void caculate() = 0;
};

//抽象的card類
class Card {
public:
    virtual void display() = 0;
};

//抽象的內存類
class Memory {
public:
    virtual void storage() = 0;
};

//架構類
class Computer {
public:
    Computer(CPU *cpu, Card *card, Memory *mem) {
        this->cpu = cpu;
        this->card = card;
        this->mem = mem;
    }

    void work() {
        this->cpu->caculate();
        this->card->display();
        this->mem->storage();
    }

    ~Computer() {
        if (this->cpu != NULL) {
            cout << "~cpu" << endl;
            delete this->cpu;
        }
        if (this->card != NULL) {
            cout << "~card"<<endl;
            delete this->card;
        }
        if (this->mem != NULL) {
            cout << "~mem"<<endl;
            delete this->mem;
        }
    }

private:
    CPU *cpu;
    Card *card;
    Memory *mem;
};
// --------------------------

//-----------實現層----------
//具體的IntelCPU
class IntelCPU : public CPU {
public:
    virtual void caculate() {
        cout << "Intel CPU開始計算了" << endl;
    }
};

class IntelCard : public Card {
public:
    virtual void display() {
        cout << "Intel Card開始顯示了" << endl;

    }
};

class IntelMem : public Memory {
public:
    virtual void storage() {
        cout << "Intel mem開始存儲了" << endl;

    }
};

class NvidiaCard : public Card {
public:
    virtual void display() {
        cout << "Nvidia 顯卡開始顯示了" << endl;
    }
};

class KingstonMem : public Memory {
public:
    virtual void storage() {
        cout << "KingstonMem 開始存儲了" << endl;
    }
};

//--------------------------

void test()
{
    Computer *com1 = new Computer(new IntelCPU, new IntelCard, new IntelMem);
    com1->work();
    delete com1;    // 如果定義一個指針不要忘記釋放

}

//--------業務層-------------------
int main() {
    //1 組裝第一台intel系列的電腦
#if 0
    CPU *intelCpu = new IntelCPU;
    Card *intelCard = new IntelCard;
    Memory *intelMem = new IntelMem;

    Computer *com1 = new Computer(intelCpu, intelCard, intelMem);

    com1->work();

    Card *nCard = new NvidiaCard;
    Memory* kMem = new KingstonMem;

    Computer *com2 = new Computer(intelCpu, nCard, kMem);

    com2->work();

    delete intelCpu;
#endif
//    Computer *com1 = new Computer(new IntelCPU, new IntelCard, new IntelMem);
//    com1->work();
//    delete com1;   // 這里不要忘記釋放
    test();
    return 0;
}
這個好好看看

 圓類

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>


using namespace std;


//抽象的圖形類
class Shape
{
public:
    //打印出圖形的基本你屬性
    virtual void show() = 0;
    //得到圖形的面積
    virtual double getArea() = 0;

    virtual ~Shape() {

    }
};

//圓類
class Circle :public Shape
{
public:
    Circle(double r) {
        this->r = r;
    }

    //打印出圖形的基本你屬性
    virtual void show()  {
        cout << "圓的半徑是 " << r << endl;
    }
    //得到圖形的面積
    virtual double getArea()  {
        cout << "獲取圓的面積" << endl;
        return this->r*this->r *3.14;
    }
    ~Circle() {
        cout << "圓的析構函數。。" << endl;
    }
private:
    double r;
};

class Square :public Shape
{
public:
    Square(double a) {
        this->a = a;
    }

    //打印出圖形的基本你屬性
    virtual void show() {
        cout << "正方形的邊長是" << this->a << endl;
    }
    //得到圖形的面積
    virtual double getArea() {
        cout << "得到正方形的面積" << endl;
        return a*a;
    }


    ~Square() {
        cout << "正方形的析構函數" << endl;
    }
private:
    double a;
};



int main(void)
{
    Shape *array[2] = { 0 };

    for (int i = 0; i < 2; i++) {
        //生成一個圓
        if (i == 0) {
            double r;
            cout << "請輸入圓的半徑" << endl;
            cin >> r;
            array[i] = new Circle(r);
        }
        //生成一個正方形
        else {
            double a;
            cout << "請輸入正方形的邊長" << endl;
            cin >> a;
            array[i] = new Square(a);
        }
    }


    //遍歷這個array數組
    for (int i = 0; i < 2; i++) {
        array[i]->show();
        cout << array[i]->getArea() << endl;

        delete array[i];
    }

    return 0;
}
圓類

比較大的例子,推薦看看

多態練習, 企業信息管理

https://i.cnblogs.com/Files.aspx 

 函數指針與多態

函數指針基礎

#include <cstdio>
#include <cstdlib>
#include <cstring>

// 數組類型, 基本語法知識梳理

// 定義一個數組類型
// int arr[10];

// 定義一個指針數組類型

// 定義一個指向 數組類型的指針, 數組類的指針
int main() {
    int a[10];  // a代表數組首元素地址, &a 代表整個數組的地址
    // a+1 代表步長加4個單元,就是a[1]的地址,
    // &a+1 代表步長加40個單元后的地址

    //
    {
        // 定義數組類型
        typedef int (myTypeArr)[10];
        myTypeArr myArr;
        myArr[0] = 10;
        printf("%d\n", myArr[0]);
    }

    {
        // 定義一個指針數組類型
        typedef int (*PTypeArray)[10]; // int * p
        PTypeArray myPArray;  // sizeof(int) * 10; 步長, myPArray 是一個二級指針

        myPArray = &a;
        // 相當於
        // int b = 10;
        // int *c = NULL;
        // c = &b;

        (*myPArray)[0] = 20;  // 注意這里
        printf("%d\n", a[0]);
    }

    {
        // 定義一個指向 數組類型的指針, 數組類的指針

        int (*MyPoint)[10]; // 告訴c編譯器給我分配內存
        MyPoint = &a;

        (*MyPoint)[0] = 40;

        printf("%d\n", a[0]);

    }
    return 0;
}

// 函數指針語法知識梳理
// 如何定義一個函數類型
// 如何定義一個函數指針類型
// 如何定義一個函數指針(指向一個函數入口地址)

int add(int a, int b) {
    printf("func add...\n");
    return a + b;
}

int main01() {
    add(1, 2); // 直接調用// 函數名就是函數入口地址

    // 如何定義一個函數類型
    {
        typedef int (myFuncType)(int a, int b); // 定義一個類型
        myFuncType *myFuncTypePoint = NULL; // 定義了一個指針,指向某一種類的函數

        myFuncTypePoint = &add; // 細節,這里的&add &可以不加
        myFuncTypePoint(1, 2);  // 間接調用
    }

    // 定義一個函數指針類型
    {
        typedef int (*MyPointFuncType)(int a, int b); // int *a = NULL;
        MyPointFuncType myPointFunc = NULL; // 定義一個指針
        myPointFunc = add;
        myPointFunc(1, 2);
    }

    // 函數指針
    {
        int (*myPointFunc)(int a, int b); // 定義一個變量
        myPointFunc = add;
        myPointFunc(1, 2);
    }

}
函數指針基礎
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int myAdd(int a, int b)
{
    printf("func myAdd...\n");
    return a + b;
}
int myAdd2(int a, int b)   // 相當於虛函數表
{
    printf("func myAdd2...\n");
    return a + b;
}

// 定義了一個類型
typedef int (*myTypeFuncAdd)(int a, int b);


// mainop都是函數指針做函數參數
int mainop(myTypeFuncAdd myFuncAdd)
{
    int c = myFuncAdd(1, 2); // 間接調用
    printf("mainop\n");

    return c;
}
// int (*myTypeFuncAdd)(int a, int b);  定義一個指向某種函數的指針
int mainop2(int (*myTypeFuncAdd)(int a, int b))
{
    int c = myTypeFuncAdd(1, 2);  // 間接調用
    printf("mainop2\n");

    return c;
}

// 間接調用
// 任務的調用,和任務的編寫可以分開
int main()
{
    /*
    myTypeFuncAdd myfuncAdd = myAdd;

    myfuncAdd(1, 3);  // 直接調用

    mainop(myAdd);
    mainop2(myAdd);
    */

    mainop(myAdd);
    mainop(myAdd2);  // 相當於發生了多態,函數指針做函數參數
    return 0;
}
函數指針做函數參數思想
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>


using namespace std;

int func(int a, int b)
{
    cout << " 1999 年寫的 func" << endl;

    return 0;
}

int func2(int a, int b)
{
    cout << "1999 寫的 func2" << endl;
    return 0;
}

int func3(int a, int b) 
{
    cout << "1999年 寫的 func3 " << endl;
    return 0;
}

//2018想添加一個新的子業務
int new_func4(int a, int b)
{
    cout << "2018 新寫的子業務" << endl;
    cout << "a = " << a << ", b = " << b << endl;
    return 0;
}

//方法一:  函數的返回值, 函數的參數列表(形參的個數,類型,順序)
//定義一個函數類型。

typedef int(FUNC)(int, int);

//方法二:   定義一個函數指針
typedef int(*FUNC_P)(int, int);


//定義一個統一的接口 將他們全部調用起來。

void my_funtion(int(*fp)(int, int), int a, int b)
{
    cout << "1999年實現這個架構業務" << endl;
    cout << "固定業務1" << endl;
    cout << "固定業務2" << endl;

    fp(a, b);//可變的業務

    cout << "固定業務3" << endl;

}

int main(void)
{
#if 0
    //方法一:
    FUNC *fp = NULL;

    fp = func;
    fp(10, 20);

    FUNC_P fp2 = NULL;

    fp2 = func;

    fp2(100, 200);

    //方法三:
    int(*fp3)(int, int)   = NULL;
    fp3 = func;
    fp3(1000, 3000);
#endif
    my_funtion(func, 10, 20);
    my_funtion(func2, 100, 200);
    my_funtion(func3, 1000, 2000);

    my_funtion(new_func4, 2000, 3000);
    
    return 0;
}
函數指針的工程意義
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>


using namespace std;
//-------------抽象層------------
//定義拆開錦囊方法的類型。
typedef void(TIPS)(void);

//定義錦囊
struct tip
{
    char from[64]; //誰寫的
    char to[64];//寫給誰的。
    //錦囊的內容
    TIPS *tp;//相當於抽象類的 純虛函數.
};

//需要一個打開錦囊的架構函數
void open_tips(struct tip *tip_p)
{
    cout << "打開了錦囊" << endl;
    cout << "此錦囊是由" << tip_p->from << "寫給 " << tip_p->to << "的。" << endl;
    cout << "內容是" << endl;
    tip_p->tp(); //此時就發生了多態現象。
}

//提供一個創建一個錦囊的方法
struct tip* create_tip(char*from, char *to, TIPS*tp)
{
    struct tip *temp = (struct tip*)malloc(sizeof(struct tip));
    if (temp == NULL) {
        return NULL;
    }
    strcpy(temp->from, from);
    strcpy(temp->to, to);
    //給一個回調函數賦值, 一般稱 注冊回調函數
    temp->tp = tp;

    return temp;
}

//提供一個銷毀錦囊的方法
void destory_tip(struct tip *tp)
{
    if (tp != NULL) {
        free(tp);
        tp = NULL;
    }
}


// ------------- 實現層------------
//諸葛亮寫了3個錦囊
void tip1_func(void)
{
    cout << "一到東吳就拜會喬國老" << endl;
}

void tip2_func(void)
{
    cout << "如果主公樂不思蜀,就謊稱曹賊來襲。趕緊回來 " << endl;
}

void tip3_func(void)
{
    cout << "如果被孫權追殺,向孫尚香求救" << endl;
}

void tip4_func(void)
{
    cout << "如果求救孫尚香都不靈,  你們去死了, 我是蜀國老大了" << endl;
}


//---------------  業務層-----------------
int main(void)
{
    //創建出3個錦囊
    struct tip *tip1 = create_tip("孔明", "趙雲", tip1_func);
    struct tip *tip2 = create_tip("孔明", "趙雲", tip2_func);
    struct tip *tip3 = create_tip("孔明", "趙雲", tip3_func);
    struct tip *tip4 = create_tip("龐統", "趙雲", tip4_func);

    //由趙雲進行拆錦囊。
    cout << "剛剛來到東吳, 趙雲打開第一個錦囊" << endl;
    open_tips(tip1);
    cout << "-----------" << endl;

    cout << "劉備樂不思蜀, 趙雲打開第二個錦囊" << endl;
    open_tips(tip2);
    cout << "-----------" << endl;

    cout << "孫權大軍追殺,趙雲打開第三個錦囊" << endl;
    open_tips(tip3);
    cout << "-----------" << endl;

    cout << "趙雲發現,實在是殺不動了, 打開了第四個錦囊" << endl;
    open_tips(tip4);

    destory_tip(tip1);
    destory_tip(tip2);
    destory_tip(tip3);
    destory_tip(tip4);
    
    return 0;
}
諸葛亮錦囊妙計

未完待續....

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM