网易云课堂_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. 但不能将基类对象加长,无中生有变出派生类对象

 

转载于:https://www.cnblogs.com/denggelin/p/5926279.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值