C++实用教程(四):面向对象核心多态 笔记

推荐B站视频:C++现代实用教程(四):面向对象核心多态_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV15v4y1M7fF/?spm_id_from=333.999.0.0&vd_source=a934d7fc6f47698a29dac90a922ba5a3

本项目通用的tasks.json文件和launch.json

  • tasks.json
{
    "version": "2.0.0",
    "options": {
        "cwd": "${workspaceFolder}/build"
    },
    "tasks": [
        {
            "type": "shell",
            "label": "cmake",
            "command": "cmake",
            "args": [
                ".."
            ]
        },
        {
            "label": "make",
            "group": "build",
            "command": "make",
            "args": [],
            "problemMatcher": []
        },
        {
            "label": "Build",
            "dependsOrder": "sequence",
            "dependsOn": [
                "cmake",
                "make"
            ]
        },
        {
            "type": "cppbuild",
            "label": "C/C++: g++ 生成活动文件",
            // "command": "/usr/bin/g++",
            "command": "D://mingw64//bin//g++.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "-o",
                "${workspaceFolder}/bin/app.exe",
                // "-finput-charset=UTF-8",
                /*
                    -fexec-charset指定输入文件的编码格式
                    -finput-charset指定生成可执行的编码格式,
                */
                "-finput-charset=GBK",  // 处理mingw中文编码问题
                "-fexec-charset=GBK"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "D://mingw64//bin//g++.exe"
        }
    ]
}
  • launch.json
{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) 启动",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/bin/app.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true,
                },
            ],
            "preLaunchTask": "Build",
            "miDebuggerPath": "D://mingw64//bin//gdb.exe", // 修改为你的 gdb 路径
        },
    ]
}
  • CMakeLists.txt
​cmake_minimum_required(VERSION 3.28.0)
project(project)
include_directories(${PROJECT_SOURCE_DIR}/include)
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_LIST)

add_executable(app main.cpp ${SRC_LIST})
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

​

C++的面向对象与很多其他的面向对象语言有很多不同,本质的原因是在于C++是一门极其重视性能的编程语言

>>多态是面向对象的核心

  • 这一点对于C++来说也不例外
  • 面向对象三大特性为:封装、继承、多态
    • 本人不是很喜欢C++的继承,其实是不喜欢继承
    • 封装和继承基本上是为多态而准备的
  • 面向对象是使用多态性获得对系统中每个源代码依赖项的绝对控制的能力的(大牛说的)
  • 高内聚、低耦合是程序设计的目标(无论是否面向对象,),而多态是实现高内聚,低耦合的基础

>>目录

    1.多态与静态绑定

    2.虚函数与动态绑定

    3.多态对象的应用场景与大小

    4.Override与Final

    5.Overloading与多态

    6.析构函数与多态

    7.Dynamic_cast类型转换

    8.typeid操作符

    9.纯虚函数与抽象类

    10.接口式抽象类

第一节:多态与静态绑定

>>多态:

  • 在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口
  • 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数

>>静态绑定:将名称绑定到一个固定的函数定义,然后在每次调用该名称时执行该定义,这个也是常态执行的方式

  • shape.h 
#ifndef SHAPE_H
#define SHAPE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape{
public:
    Shape() = default;
    ~Shape() = default;
    Shape(std::string_view name);
    void draw() const {
        std::cout<<"Shape Drawing "<<m_name<<std::endl;
    }
protected:
    std::string m_name;
};
#endif
  • shape.cpp
#include "shape.h"

Shape::Shape(std::string_view name) : m_name(name){

}
  • rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <string>
#include <string_view>
#include <iostream>
#include "shape.h"
class Rectangle:public Shape{
public:
    Rectangle() = default;
    ~Rectangle() = default;
    Rectangle(double x,double y,std::string_view name);

    void draw() const {
        std::cout<<"Rectangle Drawing "<<m_name<<" with x: "<<get_x()<<",y: "<<get_y()<<std::endl;
    }
protected:
    double get_x() const{
        return m_x;
    }
    double get_y() const{
        return m_y;
    }
private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif
  • rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y,std::string_view name)
    : Shape(name),m_x(x),m_y(y)
{

}
  • square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "rectangle.h"
class Square:public Rectangle{
public:
    Square() = default;
    ~Square() = default;
    Square(double x,std::string_view name);

    void draw() const {
        std::cout<<"Square Drawing "<<m_name<<" with x: "<<get_x()<<std::endl;
    }
};
#endif
  • square.cpp
#include "square.h"

Square::Square(double x, std::string_view name)
    : Rectangle(x,x,name)
{
}
  • main.cpp
#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

// Shape -> Rectangle -> Square
// draw()
void test1() {
    // 静态绑定的不足
    Shape s1("Shape1");
    s1.draw();// Shape Drawing Shape1

    Rectangle r1(1.0,2.0,"Rectangle1");
    r1.draw();// Rectangle Drawing Rectangle1 with x: 1,y: 2

    Square sq1(3.0,"Square1");
    sq1.draw();// Square Drawing Square1 with x: 3

    // Base Pointer
    Shape* shape_ptr = &s1;
    shape_ptr->draw();// Shape Drawing Shape1
    shape_ptr = &r1;
    shape_ptr->draw();// Shape Drawing Rectangle1
    shape_ptr = &sq1;
    shape_ptr->draw();// Shape Drawing Square1
}

int main(int argc,char* argv[]) {
    test1();
    cout<<"over~"<<endl;
    return 0;
}
  •  执行结果:
PS D:\Work\C++UserLesson\cppenv\static_bind\build> ."D:/Work/C++UserLesson/cppenv/static_bind/bin/app.exe"
Shape Drawing Shape1
Rectangle Drawing Rectangle1 with x: 1,y: 2
Square Drawing Square1 with x: 3
Shape Drawing Shape1
Shape Drawing Rectangle1
Shape Drawing Square1
over~
PS D:\Work\C++UserLesson\cppenv\static_bind\build>

第二节:虚函数与动态绑定

>>动态绑定

  • 实现继承
  • 父类、子类需要动态绑定的函数设置为虚函数
  • 创建父类指针/引用(推荐指针)指向子类对象,然后调用

>>虚函数

  • 虚函数是应在派生类中重新定义的成员函数
  • 关键字为virtual

通过例子来实现多态

  • shape.h
#ifndef SHAPE_H
#define SHAPE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape{
public:
    Shape() = default;
    ~Shape() = default;
    Shape(std::string_view name);
    virtual void draw() const {
        std::cout<<"Shape Drawing "<<m_name<<std::endl;
    }
protected:
    std::string m_name;
};
#endif
  • shape.cpp
#include "shape.h"

Shape::Shape(std::string_view name) : m_name(name){

}
  • rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <string>
#include <string_view>
#include <iostream>
#include "shape.h"
class Rectangle:public Shape{
public:
    Rectangle() = default;
    ~Rectangle() = default;
    Rectangle(double x,double y,std::string_view name);

    virtual void draw() const {
        std::cout<<"Rectangle Drawing "<<m_name<<","<<"x: "<<get_x()<<",y: "<<get_y()<<std::endl;
    }
protected:
    double get_x() const{
        return m_x;
    }
    double get_y() const{
        return m_y;
    }
private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif
  • rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y,std::string_view name)
    : Shape(name),m_x(x),m_y(y)
{

}
  • square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "rectangle.h"
class Square:public Rectangle{
public:
    Square() = default;
    ~Square() = default;
    Square(double x,std::string_view name);

    void draw() const {
        std::cout<<"Square Drawing "<<m_name<<" with x: "<<get_x()<<std::endl;
    }
};
#endif
  • square.cpp
#include "square.h"

Square::Square(double x, std::string_view name)
    : Rectangle(x,x,name)
{
}
  • 执行结果:
PS D:\Work\C++UserLesson\cppenv\dynamic_bind\bin> ."D:/Work/C++UserLesson/cppenv/dynamic_bind/bin/app.exe"
Shape Drawing Shape1
Rectangle Drawing Rectangle1,x: 1,y: 2
Square Drawing Square1 with x: 3
over~
PS D:\Work\C++UserLesson\cppenv\dynamic_bind\bin>

第三节:多态对象的应用场景与大小

3.1 多态对象的应用场景

(1)多态的两个应用场景

  • 函数
  • 存储进入Collections

(2)多态与Collection

  • 可以存储值类型(并不满足多态)
  • 可以存储指针类型
  • 不可以存储引用

(3)通过例子来

  • 多态的两大应用场景

 注意:这里的源文件和头文件和第二节的一样!

  • Base Pointers
#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape* s) {
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
void test1() {
    Shape s1("Shape1");
    Rectangle r1(1.0,2.0,"Rectangle1");
    Square sq1(3.0,"Square1");

    // Base Pointers
    Shape* shape_ptr = &s1;
    draw_shape(shape_ptr);
    shape_ptr = &r1;
    draw_shape(shape_ptr);
    shape_ptr = &sq1;
    draw_shape(shape_ptr);
}

int main(int argc,char* argv[]) {
    test1();
    cout<<"over~"<<endl;
    return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin> ."D:/Work/C++UserLesson/cppenv/dynamic_application/bin/app.exe"
Shape Drawing Shape1
Rectangle Drawing Rectangle1,x: 1,y: 2
Square Drawing Square1 with x: 3
over~
PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin>

 1)可以存储值类型(并不满足多态)

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape* s) {
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
void test1() {
    Shape s1("Shape1");
    Rectangle r1(1.0,2.0,"Rectangle1");
    Square sq1(3.0,"Square1");

    // collection 
    cout<<"*********collection*********"<<endl;
    Shape shapes[]{s1,r1,sq1}; // 不符合多态
    for(Shape &s:shapes) {
        s.draw();
    }
}

int main(int argc,char* argv[]) {
    test1();
    cout<<"over~"<<endl;
    return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin> ."D:/Work/C++UserLesson/cppenv/dynamic_application/bin/app.exe"
*********collection*********
Shape Drawing Shape1
Shape Drawing Rectangle1
Shape Drawing Square1
over~
PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin>

2)可以存储指针类型

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape* s) {
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
void test1() {
    Shape s1("Shape1");
    Rectangle r1(1.0,2.0,"Rectangle1");
    Square sq1(3.0,"Square1");

    // Pointer
    cout<<"*********Pointer*********"<<endl;
    Shape* shapes_ptr[]{&s1,&r1,&sq1};
    for(Shape* s:shapes_ptr) {
        s->draw();
    }
}

int main(int argc,char* argv[]) {
    test1();
    cout<<"over~"<<endl;
    return 0;
}

执行结果:

PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin> ."D:/Work/C++UserLesson/cppenv/dynamic_application/bin/app.exe"
*********Pointer*********
Shape Drawing Shape1
Rectangle Drawing Rectangle1,x: 1,y: 2
Square Drawing Square1 with x: 3
over~
PS D:\Work\C++UserLesson\cppenv\dynamic_application\bin>

 3)不可以存储引用

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape* s) {
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
void test1() {
    Shape s1("Shape1");
    Rectangle r1(1.0,2.0,"Rectangle1");
    Square sq1(3.0,"Square1");

    // Shape ref 
    // cout<<"*********collection*********"<<endl;
    Shape &shape_ref[]{s1,r1,sq1}; //error 不允许使用引用的数组
}

int main(int argc,char* argv[]) {
    test1();
    cout<<"over~"<<endl;
    return 0;
}

3.2 多态对象的大小  

  • dynamic binding is not free
  • 下面来对比一下多dynamic binding static binding 的大小

通过例子来,演示一下打印类的信息

  • 静态就去掉virtual然后运行打印,动态就加上virtual然后运行打印
#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape *s)
{
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
int main(int argc, char *argv[])
{
    // 静态绑定的不足
    Shape s1("Shape1");
    Rectangle r1(1.0, 2.0, "Rectangle1");
    Square sq1(3.0, "Square1");
    cout << "--------------" << endl;

    cout << "sizeof(Shape) : " << sizeof(Shape) << endl;         // s 32 d 40
    cout << "sizeof(Rectangle) : " << sizeof(Rectangle) << endl; // s 48 d 56
    cout << "sizeof(Square) : " << sizeof(Square) << endl;       // s 48 d 56

    cout << "----- yz ------" << endl;
    return 0;
}

第四节:Override和Final的用法

  • Override 子类的自检功能
    • 如果父类没有override所对应的方法,则报错
  • Final为最终的意思,标识为Final子类不可重写

比方拼写错误,编译有可能还通过,这就导致错误很难排查,故有必要使用override和final

第五节:Overloading 与多态

  • Overload(重载):在C++程序中,可以将语义,功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型、顺序不同),即函数重载
  • 多态对象不能调用子类重载函数,但可以调用父类重载函数

 shape.h

#ifndef SHARE_H
#define SHARE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape
{
public:
    Shape() = default;
    ~Shape() = default;
    Shape(std::string_view name);

    virtual void draw() const
    {
        std::cout << "Shape Drawing " << m_name << std::endl;
    }
    virtual void draw(std::string_view color) const override
    {
        std::cout << "Shape Drawing " << m_name << " color " << color << std::endl;
    }

protected:
    std::string m_name;
};
#endif

rectangle.h 

#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "shape.h"

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    ~Rectangle() = default;
    Rectangle(double x, double y, std::string_view name);

    virtual void draw() const override // final
    {
        std::cout << "Rectangle Drawing " << m_name << " m_x: " << get_x() << " m_y:" << get_y() << std::endl;
    }
    virtual void draw(std::string_view color, int x) const
    {
        std::cout << "Shape Drawing " << m_name << " color " << color << " x " << x << std::endl;
    }

protected:
    double get_x() const
    {
        return m_x;
    }
    double get_y() const
    {
        return m_y;
    }

private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif

 main.cpp

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape *s)
{
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
int main(int argc, char *argv[])
{
    // 静态绑定的不足
    Shape s1("Shape1");
    Rectangle r1(1.0, 2.0, "Rectangle1");
    Square sq1(3.0, "Square1");
    cout << "--------------" << endl;

    Shape *shape_ptr = &r1;
    shape_ptr->draw();
    // 父类
    shape_ptr->draw("red");
    // shape_ptr->draw("red", 2); // error:没有参数列表匹配的重载函数

    cout << "----- yz ------" << endl;
    return 0;
}

为了防止出现这样的错误:error:没有参数列表匹配的重载函数

可以在父类Shape的draw函数加入override

virtual void draw(std::string_view color) const override{
    ...
}

第六节:析构函数与多态

  • 析构函数在值类型销毁时,会一次调用继承的析构函数
  • 但是在多态对象销毁时,只会调用父类的析构函数
    • 将析构函数设置为虚函数可以解决该问题

通过例子来,析构函数与多态

  • shape.h
#ifndef SHARE_H
#define SHARE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape
{
public:
    Shape() = default;
    virtual ~Shape();
    Shape(std::string_view name);

    virtual void draw() const
    {
        std::cout << "Shape Drawing " << m_name << std::endl;
    }
    // virtual void draw(std::string_view color) const override
    // {
    //     std::cout << "Shape Drawing " << m_name << " color " << color << std::endl;
    // }

protected:
    std::string m_name;
};
#endif
  • shape.cpp
#include "shape.h"

Shape::Shape(std::string_view name)
    : m_name(name)
{
}

Shape::~Shape()
{
    std::cout << "Shape deconstructor" << std::endl;
  •  rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "shape.h"

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    virtual ~Rectangle();
    Rectangle(double x, double y, std::string_view name);

    virtual void draw() const override // final
    {
        std::cout << "Rectangle Drawing " << m_name << " m_x: " << get_x() << " m_y:" << get_y() << std::endl;
    }
    virtual void draw(std::string_view color, int x) const
    {
        std::cout << "Shape Drawing " << m_name << " color " << color << " x " << x << std::endl;
    }

protected:
    double get_x() const
    {
        return m_x;
    }
    double get_y() const
    {
        return m_y;
    }

private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif
  •  rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y, std::string_view name)
    : Shape(name), m_x(x), m_y(y)
{
}

Rectangle::~Rectangle()
{
    std::cout << "Rectangle Deconstructor" << std::endl;
}
  • square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "rectangle.h"
class Square : public Rectangle
{
public:
    Square() = default;
    virtual ~Square();
    Square(double x, std::string_view name);
    virtual void draw() const override
    {
        std::cout << "Square Drawing " << m_name << "with x: " << get_x() << std::endl;
    }
};

#endif
  • square.cpp
#include "square.h"

Square::Square(double x, std::string_view name)
    : Rectangle(x, x, name)
{
}

Square::~Square()
{
    std::cout << "Square deconstructor" << std::endl;
}

第七节:Dynamic_cast类型转换

>>多态对象的缺点

  • 虽然可以动态地调用子类中虚函数
  • 但是却不能调用子类中的其他的函数

>>如何调用子类的其他方法

        通过dynamic_cast父类指针转换为子类指针即可

  • shape.h
#ifndef SHARE_H
#define SHARE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape
{
public:
    Shape() = default;
    virtual ~Shape();
    Shape(std::string_view name);

    virtual void draw() const
    {
        std::cout << "Shape Drawing " << m_name << std::endl;
    }
    // virtual void draw(std::string_view color) const override
    // {
    //     std::cout << "Shape Drawing " << m_name << " color " << color << std::endl;
    // }

protected:
    std::string m_name;
};
#endif
  • shape.cpp
#include "shape.h"

Shape::Shape(std::string_view name)
    : m_name(name)
{
}

Shape::~Shape()
{
    std::cout << "Shape deconstructor" << std::endl;
}
  • rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "shape.h"

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    virtual ~Rectangle();
    Rectangle(double x, double y, std::string_view name);

    virtual void draw() const override // final
    {
        std::cout << "Rectangle Drawing " << m_name << " m_x: " << get_x() << " m_y:" << get_y() << std::endl;
    }
    virtual void draw(std::string_view color, int x) const
    {
        std::cout << "Shape Drawing " << m_name << " color " << color << " x " << x << std::endl;
    }
    double get_x() const
    {
        return m_x;
    }

protected:
    // double get_x() const
    // {
    //     return m_x;
    // }
    double get_y() const
    {
        return m_y;
    }

private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif
  • rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y, std::string_view name)
    : Shape(name), m_x(x), m_y(y)
{
}

Rectangle::~Rectangle()
{
    std::cout << "Rectangle Deconstructor" << std::endl;
}
  • square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "rectangle.h"
class Square : public Rectangle
{
public:
    Square() = default;
    virtual ~Square();
    Square(double x, std::string_view name);
    virtual void draw() const override
    {
        std::cout << "Square Drawing " << m_name << "with x: " << get_x() << std::endl;
    }
};

#endif
  • square.cpp
#include "square.h"

Square::Square(double x, std::string_view name)
    : Rectangle(x, x, name)
{
}

Square::~Square()
{
    std::cout << "Square deconstructor" << std::endl;
}

main.cpp

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape *s)
{
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
int main(int argc, char *argv[])
{
    // 静态绑定的不足
    // Square sq1(3.0, "Square1");
    Shape *p = new Rectangle(1.0, 2.0, "Square1");
    p->draw();
    // cout << p->get_x() << endl; // error:类 "Shape" 没有成员 "get_x"
    Rectangle *r_p = dynamic_cast<Rectangle *>(p);
    cout << r_p->get_x() << endl;

    cout << "----- over ------" << endl;
    return 0;
}

执行结果:

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape *s)
{
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
int main(int argc, char *argv[])
{
    // 静态绑定的不足
    // Square sq1(3.0, "Square1");
    Shape *p = new Rectangle(1.0, 2.0, "Square1");
    p->draw();
    // cout << p->get_x() << endl; // error:类 "Shape" 没有成员 "get_x"
    Rectangle *r_p = dynamic_cast<Rectangle *>(p);
    cout << r_p->get_x() << endl;
    // cout << r_p->get_y() << endl;// error:函数 "Rectangle::get_y" 不可访问

    cout << "----- over ------" << endl;
    return 0;
}

第八节:typeid()操作符

  • 注意typeid是操作符,不是函数
  • 它可以在运行时获知类型名称
    • typeid(变量).name()

通过例子来

  • typeid的用法
#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape *s)
{
    s->draw();
}

// Shape -> Rectangle -> Square
// draw()
int main(int argc, char *argv[])
{
    cout << "typeid(float) : " << typeid(float).name() << endl; // msvc float 不同编译器返回不同
    cout << "typeid(int) : " << typeid(int).name() << endl;
    if (typeid(1) == typeid(int))
    {
        cout << "1 is a int " << endl;
    }
    else
    {
        cout << "1 is not a int" << endl;
    }
    Rectangle r1(1.0, 3.0, "Rectangle1");
    Shape *shape_ptr = &r1;
    Shape &shape_ref = r1;
    cout << "typeid ptr: " << typeid(shape_ptr).name() << endl;
    cout << "typeid ref: " << typeid(shape_ref).name() << endl;
    cout << "typeid *ptr: " << typeid(*shape_ptr).name() << endl;

    cout << "----- yz ------" << endl;
    return 0;
}

在实现多态的时候,可以用指针或者引用指向子类的对象,但是多态我们更希望可以操作它,所以指针比引用更常用(呵呵哒!)

第九节:纯虚函数与抽象类

(1)纯虚函数

  • virtual double func() const = 0;
  • 子类必须重写,或者继续作为抽象类来使用

(2)抽象类

  • 类中存在纯虚函数的类就叫做抽象类
  • 抽象类是不能实例化的

第十节:接口式的抽象类

  • C++中是不存在接口的关键字的
  • 但是可以模拟接口
  • 一个只有纯虚函数和没有成员变量的抽象类可以用来模拟在其他面向对象编程语言中的接口

通过例子来:

  • shape.h
#ifndef SHAPE_H
#define SHAPE_H
#include <string>
#include <string_view>
#include <iostream>
class Shape{
public:
    virtual void draw() const = 0;
};
#endif
  • rectangle.h 
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <string>
#include <string_view>
#include <iostream>
#include "shape.h"
class Rectangle:public Shape{
public:
    Rectangle() = default;
    virtual ~Rectangle();
    Rectangle(double x,double y,std::string_view name);
    virtual void draw() const override{
        std::cout<<"Rectangle Drawing "<<m_name<<","<<"x: "<<get_x()<<",y: "<<get_y()<<std::endl;
    }
    double get_x() const{
        return m_x;
    }
protected:
    std::string m_name;
    double get_y() const{
        return m_y;
    }
private:
    double m_x{0.0};
    double m_y{0.0};
};
#endif
  • rectangle.cpp
#include "rectangle.h"

Rectangle::Rectangle(double x, double y,std::string_view name)
    : m_name(name),m_x(x),m_y(y)
{

}

Rectangle::~Rectangle() {
    std::cout<<"Rectangle Deconstructor"<<std::endl;
}
  • square.h
#ifndef SQUARE_H
#define SQUARE_H
#include "rectangle.h"
class Square:public Rectangle{
public:
    Square() = default;
    ~Square();
    Square(double x,std::string_view name);

    void draw() const override{
        std::cout<<"Square Drawing "<<m_name<<" with x: "<<get_x()<<std::endl;
    }
};
#endif
  •  square.cpp
#include "square.h"

Square::Square(double x, std::string_view name)
    : Rectangle(x,x,name)
{
}

Square::~Square() {
    std::cout<<"Square Deconstructor"<<std::endl;
}

main.cpp

#include <iostream>
#include "square.h"
#include "rectangle.h"
#include "shape.h"
using namespace std;

void draw_shape(Shape* s) {
    s->draw();
}

void test1() {
    Rectangle r1(1.2,3.4,"R1");
    Shape* s_p = &r1;
    draw_shape(s_p);

    Square s1(2.2,"S1");
    s_p = &s1;
    draw_shape(s_p);
}

int main(int argc, char *argv[])
{
    test1();
    cout << "over~" << endl;
    return 0;
}

运行结果: 

PS D:\Work\C++UserLesson\cppenv\abstract_interface\bin> ."D:/Work/C++UserLesson/cppenv/abstract_interface/bin/app.exe"
Rectangle Drawing R1,x: 1.2,y: 3.4
Square Drawing S1 with x: 2.2
Square Deconstructor
Rectangle Deconstructor
Rectangle Deconstructor
over~
PS D:\Work\C++UserLesson\cppenv\abstract_interface\bin> 

相关推荐

  1. C++面向对象学习笔记

    2024-01-26 20:00:01       35 阅读
  2. C#基础——面向对象(封装 继承 )

    2024-01-26 20:00:01       64 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-26 20:00:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-26 20:00:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-01-26 20:00:01       82 阅读
  4. Python语言-面向对象

    2024-01-26 20:00:01       91 阅读

热门阅读

  1. linux shell脚本 条件语句

    2024-01-26 20:00:01       44 阅读
  2. Spring之基于注解的IOC(DI)

    2024-01-26 20:00:01       47 阅读
  3. 第七章SQL编程(持续更新中)

    2024-01-26 20:00:01       38 阅读
  4. 如何利用 chatgpt 提高查询效率

    2024-01-26 20:00:01       53 阅读
  5. 倒计时80天

    2024-01-26 20:00:01       50 阅读
  6. DNS故障的几种常见原因及解决方法

    2024-01-26 20:00:01       53 阅读
  7. K8S安全机制

    2024-01-26 20:00:01       58 阅读