zoukankan      html  css  js  c++  java
  • 网易云课堂_C++程序设计入门(上)_第6单元:丹枫虽老犹多态–继承与多态

    第01节:继承

    第02节:构造函数和析构函数

    第03节:函数重定义

    第04节:多态和虚函数

    第05节:访问控制 (可见性控制)

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

    第07节:动态类型转换

    第01节:继承

    回顾

    面向对象的4个特点:

    A(抽象) P(多态)I(继承)E(封装)

    前两个单元:AE

    本单元: PI

    第02节:构造函数和析构函数

    派生类继承的成员

    派生类继承 ctor 和 dtor 吗?

    派生类不继承的特殊函数

    构造函数 (C++11已允许继承)

    析构函数

    作为特权地位的友元函数

    赋值运算符函数

    #include <iostream>
    
    struct A
    {
    	A(int i)
    	{
    
    	}
    	A(double d, int i)
    	{
    
    	}
    	// ...
    };
    
    struct B : A
    {
    	using A::A; // 继承基类构造函数
    	int d{ 0 }; // 新的变量初始化方法
    };
    
    int main()
    {
    	B b(1); // b.d 初始化为 0
    }
    

    Calling Base Class Constructors ( 调用基类构造函数)

    Ctors of base class can only be invoked from the constructors of the derived classes. ( 基类构造函数只能由派生类构造函数调用)

    The syntax to invoke it is as follows:

    DerivedClass(parameterList) : BaseClass()
    {
    	// Perform initialization
    }
    // Or
    
    DerivedClass(parameterList) : BaseClass(argumentList)
    {
    	// Perform initialization
    }
    

    No-Arg Constructor in Base Class( 基类的无参构造函数)

    Rules for invoke constructors in derived class

    A constructor in a derived class must always invoke a constructor in its base class. (派 生类构造函数必须调用基类构造函数)

    If a base constructor is not invoked explicitly, the base class’s no-arg constructor is invoked by default. ( 若基类ctor未被显式调用,基类的无参构造函数就会被调用)

    Constructor and Destructor Chaining ( 构造和析构函数链)

    constructor chaining (构造函数链)

    Constructing an instance of a class invokes all the base class along the inheritance chain. ( 构造类实例会沿着 继承链调用所有的基类ctor)

    Invoke sequence: base first, derive next

    destructor chaining (析构函数链)

    Conversely, the destructors are automatically invoked in reverse order(dtor 与ctor正好相反)

    Invoke sequence: derive first, base next

    no-arg constructor ( 无参构造函数)

    If a class is designed to be extended, provide a no-arg constructor. (若你的类 想被别人扩展,那么就提供一个无参构造函数)

    34. 文件扩展名:头文件用.h,源文件用 .cpp (c++, cc也可)

    35. A class should be declared in a header file and defined in a source file where the name of the files match the name of the class.

    35. 类应该在头文件中声明并在源文件中定义,俩文件名字应 该与类名相同

    例如:MyClass.h, MyClass.c++

    例外的是,模板类的声明和定义都要放在头文件中

    49. Class variables should never be declared public.

    49. 类成员变量不可被声明为public

    说明:公有变量违背了C++的信息隐藏原则。例外的是, 如果class只是一个数据结构,类似C语言中的struct,则 可将类变量声明为公有

    #include <iostream>
    
    class Fruit
    {
    public:
    	Fruit()
    	{
    
    	}
    	Fruit(int id)
    	{
    
    	}
    	std::string s;
    };
    
    class Apple : public Fruit
    {
    public:
    	Apple() :Fruit()
    	{
    
    	}
    };
    
    int main()
    {
    	Apple apple;
    }
    

    第03节:函数重定义

    Redefine (hide) ( 重定义/隐藏)

    #include <iostream>
    #include <string>
    
    class GeometricObject
    {
    public:
    	std::string toString()
    	{
    		return "parent";
    	}
    };
    
    class Circle : public GeometricObject
    {
    public:
    	std::string toString()
    	{
    		return "child";
    	}
    	void g()
    	{
    		std::cout << toString();
    	}
    };
    
    int main()
    {
    	Circle circle;
    	std::cout << circle.toString();
    	circle.GeometricObject::toString();
    
    	system("pause");
    
    	return 0;
    }
    

    Redefine v.s. Overload ( 重定义与重载)

    Overload Functions (§5.7) (重载函数)

    more than one function with the same name (多个函数名字相同)

    But different in at least one of the signatures: (但至少一个特征不同)

    parameter type ( 参数类型)

    parameter number ( 参数数量)

    parameter sequence (参数顺序)

    Redefine Functions (重定义函数)

    The functions have the same signature (函数特征相同)

    Name (同名)

    Parameters (including type, number and sequence) (同参数:类型,数量和顺序)

    Return type (返回值类型)

    Defined in base class and derived class, respectively (在基类和派生类中分别定义)

    第04节:多态和虚函数

    What is Polymorphism?

    广义的多态:不同对象对于相同的消息有不同的响应,就是OOP中的 多 态性。

    截止目前:多态性有两种表现的方式

    重载:

    class C
    {
    public:
    	int f(int x);
    	int f();
    };
    

    重定义:不同的对象调用重定义 函数,表现出不同的行为

    class A
    {
    	int f()
    	{
    		return 1;
    	}
    };
    
    class B : public A
    {
    	int f()
    	{
    		return 8;
    	}
    };
    
    A x;
    B y;
    x.f();
    y.f();
    

    Binding

    联编(Binding): 确定具有多态性的 语句调用哪个函数的过程。

    Static Binding (静态联编)

    在程序编译时确定调用哪个函数

    例:函数重载

    Dynamic Binding (动态联编)

    在程序运行时,才能够确定调用哪个 函数

    用动态联编实现的多态,也称为运行 时的多态。

    How do we implement polymorphism ( 如何实现多态)

    virtual function (虚函数)

    Override (覆盖) : redefining a virtual function in a derived class. ( 在派生类中重定义一个虚函数)

    overload重载

    override覆盖

    overlord覆盖

    Polymorphism: using dynamic binding ( 动态联编)

    How to enable dynamic binding? ( 如何使得函数能够 实现动态联编)

    The function must be declared virtual in the base class. ( 基类同 名虚函数)

    The variable that references the object for the function must contain the address of the object. ( 访问对象的成员函数时,要用 指向对象的指针或者对象引用)

    Note

    If a function is defined virtual in a base class, it is automatically virtual in all its derived classes. ( 基类定义 了虚同名函数,那么派生类中的 同名函数自动变为虚函数)

    Virtual functions:

    Virtual function table (虚函数表)

    Run-time binding (运行时联编)

    More overhead in run-time than non- virtual function ( 开销大)

    class C
    {
    public:
    	virtual std::string toString()
    	{
    		return "class C";
    	}
    };
    
    class B : public C
    {
    	std::string toString()
    	{
    		return "class B";
    	}
    };
    
    class A : public B
    {
    	std::string toString()
    	{
    		return "class A";
    	}
    };
    

    Summary: static binding v.s. dynamic binding

    基类与派生类中有同名函数

    1. 通过 派生类对象访问同名函数

    静态联编

    2. 通过 基类对象的指针访问同名函数

    静态联编

    3. 通过 基类对象的指针访问同名虚函数

    动态联编

    第05节:访问控制 (可见性控制)

    The protected Keyword

    the private and public keywords to specify whether data fields and functions can be accessed from the outside of the class. ( 说明数据及函数是否可以从类外面访问)

    Private members can only be accessed from the inside of the class (私有成员只能在类 内的函数访问)

    Public members can be accessed from any other classes. (公有成员可被任何其他类访 问)

    A protected data field or a protected function in a base class can be accessed by name in its derived classes. ( 保护属性的数据或函数可被派生类成员访 问)

    #include <iostream>
    using namespace std;
    
    class B
    {
    public:
    	int i;
    protected:
    	int j;
    private:
    	int k;
    };
    
    class A : public B
    {
    public:
    	void display()
    	{
    		cout << i << endl; // Fine, cannot access it
    		cout << j << endl; // Fine, cannot access it
    		cout << k << endl; // Wrong, cannot access it
    	}
    };
    
    int main()
    {
    	A a;
    	cout << a.i << endl; // Fine, cannot access it
    	cout << a.j << endl; // Wrong, cannot access it
    	cout << a.k << endl; // Wrong, cannot access it
    	return 0;
    }
    

    1. 公有继承

    公有继承的派生类定义形式:

    class 派生类名:public 基类名

    {

    派生类新成员定义;

    };

    1. 基类成员 在派生类中的访问属性不变。

    2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;

    3. 派生类以外的其它函数 可以通过派生类的对象,访问从基类继 承的公有成员, 但不能访问从基类继承的保护成员和私有成员。

    2. 私有继承

    私有继承的派生类定义形式:

    class 派生类名:private 基类名

    {

    派生类新成员定义;

    };

    1. 基类成员 在派生类中的访问属性都变成 private。

    2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;

    3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继 承的任何成员。

    3. 保护继承

    私有继承的派生类定义形式: class 派生类名:protected 基类名

    {

    派生类新成员定义;

    };

    1. 基类成员 公有成员和保护成员在派生类中变成保护类型的,基类 的私有成员属性不变。

    2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能 访问基类的私有成员;

    3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继 承的任何成员。

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

    Abstract Classes ( 抽象类)

    classes become more specific & concrete with each new derived class. ( 派生类时,新类会越来越明确和具 体)

    move back up to the parent and ancestor , the classes become more general and less specific. ( 沿着派生类 向父类移动,类会越来越一般化和 抽象)

    Sometimes a base class is so abstract that it cannot have any specific instances. Such a class is referred to as an abstract class ( 类太抽象以至于无法实例 化就叫做抽象类)

    Abstract Functions ( 抽象函数)

    All geometric objects have: areas & perimeters

    Declare getArea() and getPerimeter() in GeometricObject class? (在几何对象类中声明 计算面积和周长的函数?)

    Is it meaningful for getArea() in GeometricObject ? (这种声明有实际意义 吗?)

    NO, the implementation is dependent on the specific type of geometric object.

    virtual double getArea() = 0;

    virtual double getPerimeter() = 0;

    Such functions are referred to as abstract functions.

    Abstract class: the class which contains abstract functions

    第07节:动态类型转换

    Dynamic Casting Example

    dynamic_cast operator cast a parameter of the GeometricObject type into a Circle type ( 将基 类类型参数转换为派 生类类型)

    then invoke the getRadius() and getDiameter() functions defined in the Circle class ( 然后调用派生类 中独有的函数)

    // A function for displaying a geometric object
    void displayGeometricObject(GeometricObject &object)
    {
    	cout << "The area is " << object.getArea() << endl;
    	cout << "The perimeter is " << object.getPerimeter() << endl;
    
    	GeometricObject *p = &object;
    
    	Circle *p1 = dynamic_cast<Circle*>(p);
    
    	if (p1 != 0)
    	{
    		cout << "The radius is " << p1->getRadius() << endl;
    		cout << "The diameter is " << p1->getDiameter() << endl;
    	}
    }
    

    Upcasting and Downcasting ( 向上/向下 转型)

    upcasting : Assigning a pointer of a derived class type to a pointer of its base class type ( 将派生类类型指针赋值给基类类型指针)

    downcasting : Assigning a pointer of a base class type to a pointer of its derived class type. ( 将基类类型指针赋值给派生类类型指针)

    Upcasting and Downcasting ( 向上/向下 转型 续)

    Upcasting can be performed implicitly without using the dynamic_cast operator. ( 上转可不适用dynamic_cast而隐式转换)

    GeometricObject *g = new Circle(1);

    Circle *c = new Circle(2);

    g = c; //Correct

    However, downcasting must be performed explicitly. ( 下转必须显式执行)

    For example, to assign p to p1, you have to use

    c = dynamic_cast<Circle *>(g);

    typeid operator (typeid 运算符)

    How to obtain the information about the class of the object? (如何获取对象所 属的类的信息)

    typeid operator: return a reference to an object of class type_info. (typeid运算 符返回一个type_info对象的引用)

    to display the class name for object x. (显示对象x的类名)

    #include <iostream>
    #include <string>
    
    int main()
    {
    	std::string x;
    
    	std::cout << typeid(x).name() << std::endl;
    
    	system("pause");
    
    	return 0;
    }
    

    基类对象和派生类对象的互操作

    问题1:对象内存布局

    GeometricObject G;

    Circle C;

    GeometricObject* pG=&G;

    Circle* pC=&C;

    问题2:互操作

    G=C; //Y

    C=G; //N

    pG=&C; //Y

    pC=&G; //N

    GeometricObject &rG=C; //Y

    Circle &rC=G; //N

    Warning

    1. 可将派生类对象截断,只使用继承来的信息

    2. 但不能将基类对象加长,无中生有变出派生类对象

  • 相关阅读:
    中英文对照 —— 宗教
    十万个为什么 —— 冷知识
    十万个为什么 —— 冷知识
    OpenCV调试利器——Image Watch插件的安装和使用
    matlab 读写其他格式数据文件(excel)
    matlab 读写其他格式数据文件(excel)
    文学创作的艺术手法
    文学创作的艺术手法
    常用cl命令参数解释
    网站的栏目和目录结构规划方法
  • 原文地址:https://www.cnblogs.com/denggelin/p/5926279.html
Copyright © 2011-2022 走看看