C++中的封装、继承、多态理解

封装(encapsulation):就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成”类”,其中数据和函数都是类的成员。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,特定的访问权限来使用类的成员。封装可以隐藏实现细节,使得代码模块化。

继承(inheritance):C++通过类派生机制来支持继承。被继承的类型称为基类或超类,新产生的类为派生类或子类。保持已有类的特性而构造新类的过程称为继承。在已有类的基础上新增自己的特性而产生新类的过程称为派生。继承和派生的目的是保持已有类的特性并构造新类。继承的目的:实现代码重用。派生的目的:实现代码扩充。三种继承方式:public、protected、private。

继承时的构造函数:(1)、基类的构造函数不能被继承,派生类中需要声明自己的构造函数;(2)、声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,自动调用基类构造函数完成;(3)、派生类的构造函数需要给基类的构造函数传递参数;(4)、单一继承时的构造函数:派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(参数表) {本类成员初始化赋值语句;};(5)、当基类中声明有默认形式的构造函数或未声明构造函数时,派生类构造函数可以不向基类构造函数传递参数;(6)、若基类中未声明构造函数,派生类中也可以不声明,全采用缺省形式构造函数;(7)、当基类声明有带形参的构造函数时,派生类也应声明带形参的构造函数,并将参数传递给基类构造函数;(8)、构造函数的调用次序:A、调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右);B、调用成员对象的构造函数,调用顺序按照它们在类中的声明的顺序;C、派生类的构造函数体中的内容。

继承时的析构函数:(1)、析构函数也不被继承,派生类自行声明;(2)、声明方法与一般(无继承关系时)类的析构函数相同;(3)、不需要显示地调用基类的析构函数,系统会自动隐式调用;(4)、析构函数的调用次序与构造函数相反。

同名隐藏规则:当派生类与基类中有相同成员时:(1)、若未强行指名,则通过派生类对象使用的是派生类中的同名成员;(2)、如要通过派生类对象访问基类中被覆盖的同名成员,应使用基类名限定:基类名::数据成员名。

虚基类:作用:(1)、主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题;(2)、为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝。

继承、组合:组合是将其它类的对象作为成员使用,继承是子类可以使用父类的成员方法。(1)、A继承B,说明A是B的一种,并且B的所有行为对A都有意义;(2)、若在逻辑上A是B的“一部分”,则不允许B从A派生,而是要用A和其它东西组合出B;(3)、继承属于”白盒”复用,组合属于”黑盒”复用。

多态(Polymorphic)性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数。C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖或者称为重写。而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并产生代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译期间确定,需要在运行时才确定,这就是属于晚绑定。

封装可以使得代码模块化,继承可以扩展已存在的代码,它们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说不论传递过来的究竟是哪个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“= 0”。为了方便使用多态特性,常常需要在基类中定义虚函数,在很多情况下,基类本身生成对象是不合情理的。为了解决这些问题,引入了纯虚函数的概念,将函数定义为纯虚函数,则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚函数的类称为抽象类,它不能生成对象。由于纯虚函数所在的类中没有它的定义,在该类的构造函数和析构函数中不允许调用纯虚函数,否则会导致程序运行错误,但其它成员函数可以调用纯虚函数。

C++支持两种多态性:(1)、编译时多态性(静态多态,在编译时就可以确定对象使用的形式):通过重载函数实现;(2)、运行时多态性(动态多态,其具体引用的对象在运行时才能确定):通过虚函数实现。

C++中,实现多态有以下方法:虚函数、抽象类、重载、覆盖、模板。

函数重载(Overload):指在相同作用域里(如同一类中),函数同名不同参,返回值则不用理会,不同参可以是不同个数,也可以是不同类型。效果:根据实参的个数和类型调用对应的函数体。

函数覆盖(Override)(函数重写):指派生类中的函数覆盖基类中的同名同参虚函数,因此作用域不同。效果:基类指针或引用访问虚函数时会根据实例的类型调用对应的函数。

函数隐藏(Hide):对于子类中与基类同名的函数,如果不是覆盖那就成了隐藏。两种情况:(1)、同名不同参;(2)、同名同参但基类不是virtual函数。

派生类的构造函数使用说明:(1)、在派生类构造函数中,只要基类不是仅使用无参的默认构造函数,都要显示的给出基类名称参数表;(2)、基类没有定义构造函数,派生类也可以不定义,使用默认构造函数;(3)、基类有带参构造函数,派生类必须定义构造函数。

虚函数的重载函数仍是虚函数。在派生类重定义虚函数时必须有相同的函数原型,包括返回类型、函数名、参数个数、参数类型的顺序必须相同。虚函数必须是类的成员函数,不能为全局函数,也不能为静态函数。不能将友员说明为虚函数,但虚函数可以是另一个类的友员。析构函数可以是虚函数,但构造函数不能为虚函数。一般地讲,若某类中定义有虚函数,则其析构函数也应当说明为虚函数。特别是在析构函数需要完成一些有意义的操作,比如释放内存时,尤其应当如此。在类系统中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求。当然也可以像调用普通成员函数那样利用对象名来调用一个函数。若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码。

抽象类:如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类。抽象类不仅包括纯虚函数,也可包括虚函数。抽象类中的纯虚函数可能是在抽象类中定义的,也可能是从它的抽象基类中继承下来且重定义的。抽象类有一个重要特点,即抽象类必须用作派生其它类的基类,而不能用于直接创建对象实例。抽象类不能直接创建对象的原因是其中有一个或多个函数没有定义,但仍可使用指向抽象类的指针支持运行时多态性。派生类中必须重载基类中的纯虚函数,否则它仍将被看作一个抽象类。从基类继承来的纯虚函数,在派生类中仍是虚函数。

虚函数表:虚函数是通过一张虚函数表来实现的。简称为V-Table,在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得有无重要了,它就像一个地图一样,指明了实际所应该调用的函数。

一个多态的例子:

#include <iostream>
using namespace std;

class A
{
public:
	void foo()
	{
		printf("1\n");
	}

	virtual void fun()
	{
		printf("2\n");
	}
};

class B : public A
{
public:
	void foo()
	{
		printf("3\n");
	}

	void fun()
	{
		printf("4\n");
	}
};

int main(void)
{
	A a;
	B b;

	A* p = &a;
	p->foo();//1
	p->fun();//2

	p = &b;
	p->foo();//1
	p->fun();//4

	B* ptr = (B*)&a;
	ptr->foo();//3
	ptr->fun();//2

	return 0;
}

另一个例子:

#include <iostream>
using namespace std;

int main(void)
{
	class CA
	{
	public:
		virtual ~CA() {cout<<"delete CA"<<endl;}
		virtual int GetValue() {return 1;}
	};

	class CB : public CA
	{
	public:
		~CB() {cout<<"delete CB"<<endl;}
		virtual int GetValue() {return 2;}
	};

	CA* pA = new CB;
	cout<<pA->GetValue()<<endl;
	delete pA;

	/* result:
		2
		delete CB
		delete CA
	*/
	/*若父类CA中没有将析构函数定义为虚函数,则result:
		2
		delete CA
		由结果看出,如果不将父类CA的析构函数定义为虚函数,则不会调用到子类的析构函数
	*/
	/*若父类CA中的成员函数GetValue没有定义为虚函数,则result:
		1
		delete CA
	*/

	return 0;
}

对C++继承,封装,多态的理解

用了C++一段时间,感觉对C++慢慢有了一点认识,在这和大家分享一下。
C++是一款面向对象的语言,拥有面向对象语言的三大核心特性:继承,封装,多态。每一个特性的良好理解与使用都会为我们的编程带来莫大的帮助。下面我就这三个特性讲一下我对C++的理解。

继承

学过面向对象语言的人基本都可以理解什么是继承,但我们为什么要使用继承?
      很多人说继承可以使代码得到良好的复用,当然这个是继承的一个优点,但代码复用的方法除了继承还有很多,而且有些比继承更好。我认为使用继承最重要的原因是继承可以使整个程序设计更符合人们的逻辑,从而方便的设计出想要表达的意思。比如我们要设计一堆苹果,橘子,梨等水果类,使用面向对象的方法,我们首先会抽象出一个水果的基类,而后继承这个基类,派生出具体的水果类。如果要设计的水果很多,我们还可以在水果基类基础上,继续生成新的基类,比如热带水果类,温带水果类,寒带水果类等,而后再继承这些基类。这样的设计思想就相当于人类的分类思想,简单易懂,而且设计出来的程序层次分明,容易掌握。
      既然继承这么好,那该如何使用继承?
      继承虽好但不能滥用,否则设计出来的程序会杂乱不堪。根据上面的介绍,可以发现继承主要用来定义一个东西是什么,比如热带水果是水果,菠萝是热带水果等,即继承主要用来设计一个程序的类的框架,将所要设计的东西用继承来设立一个基本结构。如果想为一个类添加一个行为或格外的功能,最好是使用组合的方式。如果想了解组合的方式,可以看一下比较著名的策略模式。

封装

封装是什么?
      在C++中,比较狭隘的解释就是将数据与操作数据的方法放在一个类中,而后给每个成员设置相应的权限。从大一点的角度来说,封装就是将完成一个功能所需要的所有东西放在一起,对外部只开放调用它的接口。
       为什么要封装?
       我认为模块化设计是封装的本质原因。
       对软件设计或其他工程设计,特别是比较复杂的工程,一般都是模块化设计。模块化设计的好处就是可以将一个复杂的系统拆分成不同的模块。每一个模块又可以独立的设计,调试,这就让多人一起做一个复杂的工程成为现实。如果想做到模块化设计,封装是不可缺少的一部分。一个好的模块,比如一块inter的CPU芯片,它有强大的功能,虽然我们不知道它内部是如何实现的,但却可以很好的使用它。

多态

什么是多态?
        多态简单的说就是“一个函数,多种实现”,或是“一个接口,多种方法”。多态性表现在程序运行时根据传入的对象调用不同的函数。
        C++的多态是通过虚函数来实现的,在基类中定义一个函数为虚函数,该函数就可以在运行时,根据传入的对象调用不同的实现方法。而如果该函数不设为虚函数,则在调用的过程中调用的函数就是固定的。比如下面一个例子

//
//定义一个Duck基类,而后继承Duck派生出一个RedHandDuck类。
//其中display()方法,第一次运行设为普通函数,第二次设为虚函数

#include "iostream"

class Duck {

public:
	Duck(){}
	~Duck(){}

	//定义一个虚函数display
	virtual void display(){

		std::cout<<" I am a Duck !"<<std::endl;
	}
};

class RedHandDuck:public Duck{

public:
	RedHandDuck(){}
	~RedHandDuck(){}

	//重写display
	void display(){

		std::cout<<" I am a RedHandDuck !"<<std::endl;
	}
};

int main(){

	RedHandDuck* duck1 = new RedHandDuck();
	Duck* duck2 = duck1;

	duck1->display();
	duck2->display();

	std::getchar();
}

第一次运行结果(不使用虚函数):

第二次运行结果(使用虚函数):

由结果可以看到,由于虚函数的使用,Duck对象(可以理解为接口),调用的display()方法是根据传入的对象决定的。这就实现了“一个接口,多种方法”。

从网上看到一个关于多态的介绍,非常精辟,分享给大家

  多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。

(0)

相关推荐

  • C++多继承多态的实例详解

    C++多继承多态的实现 如果一个类中存在虚函数,在声明类的对象时,编译器就会给该对象生成一个虚函数指针,该虚函数指针指向该类对应的虚函数表. 多态的实现是因为使用了一种动态绑定的机制,在编译期间不确定调用函数的地址,在调用虚函数的时候,去查询虚函数指针所指向的虚函数表. 派生类生成的对象中的虚函数指针指向的是派生类的虚函数表,因此无论是基类还是派生来调用,都是查询的是派生类的表,调用的是派生类的函数. 如果发生了多继承,多个基类中都有虚函数,那么该是怎样的呢?虚函数指针如何排列,多个基类的指针为

  • C++中的多态与虚函数的内部实现方法

    1.什么是多态 多态性可以简单概括为"一个接口,多种行为". 也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可以用自己的方式去响应共同的消息.所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数.这是一种泛型技术,即用相同的代码实现不同的动作.这体现了面向对象编程的优越性. 多态分为两种: (1)编译时多态:主要通过函数的重载和模板来实现. (2)运行时多态:主要通过虚函数来实现. 2.几个相关概念 (1)覆盖.

  • C++中继承与多态的基础虚函数类详解

    前言 本文主要给大家介绍了关于C++中继承与多态的基础虚函数类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 虚函数类 继承中我们经常提到虚拟继承,现在我们来探究这种的虚函数,虚函数类的成员函数前面加virtual关键字,则这个成员函数称为虚函数,不要小看这个虚函数,他可以解决继承中许多棘手的问题,而对于多态那他更重要了,没有它就没有多态,所以这个知识点非常重要,以及后面介绍的虚函数表都极其重要,一定要认真的理解~ 现在开始概念虚函数就又引出一个概念,那就是重写(覆

  • c++语言中虚函数实现多态的原理详解

    前言 自上一个帖子之间跳过了一篇总结性的帖子,之后再发,今天主要研究了c++语言当中虚函数对多态的实现,感叹于c++设计者的精妙绝伦 c++中虚函数表的作用主要是实现了多态的机制.首先先解释一下多态的概念,多态是c++的特点之一,关于多态,简而言之就是 用父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种方法呢,可以让父类的指针具有多种形态,也就是说不需要改动很多的代码就可以让父类这一种指针,干一些很多子类指针的事情,这里是从虚函数的实现机制层面进行研究 在写这篇帖子之前

  • C++多态的实现机制深入理解

    在面试过程中C++的多态实现机制经常会被面试官问道.大家清楚多态到底该如何实现吗?下面小编抽空给大家介绍下多态的实现机制. 1. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数. 2. 存在虚函数的类都有一个一维的虚函数表叫做虚表.类的对象有一个指向虚表开始的虚指针.虚表是和类对应的,虚表指针是和对象对应的. 3. 多态性是一个接口多种实现,是面向对象的核心.分为类的多态性和函数的多态性. 4. 多态用虚函数来实现,结合动态绑定. 5. 纯虚函数是虚函数再加上= 0. 6.

  • 详解C++ 多态的实现及原理

    C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数.如果对象类型是派生类,就调用派生类的函数:如果对象类型是基类,就调用基类的函数 1:用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数. 2:存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针.虚表是和类对应的,虚表指针是和对象对应的. 3:多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态

  • 详解C++编程的多态性概念

    多态性(polymorphism)是面向对象程序设计的一个重要特征.如果一种语言只支持类而不支持多态,是不能被称为面向对象语言的,只能说是基于对象的,如Ada.VB就属此类.C++支持多态性,在C++程序设计中能够实现多态性.利用多态性可以设计和实现一个易于扩展的系统. 顾名思义,多态的意思是一个事物有多种形态.多态性的英文单词polymorphism来源于希腊词根poly(意为"很多")和morph(意为"形态").在C ++程序设计中,多态性是指具有不同功能的函

  • 深入解析C++中的虚函数与多态

    1.C++中的虚函数C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态",这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Tab

  • C++ 通过指针实现多态实例详解

     C++ 通过指针实现多态实例详解 1.父类(DBConnector) 1)DBConnector.h #include <string> using namespace std; class DBConnector { private: string name; public: DBConnector(); DBConnector(string _name); ~DBConnector(); void show(); }; 2)DBConnector.cpp #include "D

  • C语言实现C++继承和多态的代码分享

    这个问题主要考察的是C和C++的区别,以及C++中继承和多态的概念. C和C++的区别 C语言是面向过程的语言,而C++是面向对象的过程. 什么是面向对象和面向过程? 面向过程就是分析解决问题的步骤,然后用函数把这些步骤一步一步的进行实现,在使用的时候进行一一调用就行了,注重的是对于过程的分析.面向对象则是把构成问题的事进行分成各个对象,建立对象的目的也不仅仅是完成这一个个步骤,而是描述各个问题在解决的过程中所发生的行为. 面向对象和面向过程的区别? 面向过程的设计方法采用函数来描述数据的操作,

  • 深入理解C++的多态性

    C++编程语言是一款应用广泛,支持多种程序设计的计算机编程语言.我们今天就会为大家详细介绍其中C++多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握. 多态性可以简单地概括为"一个接口,多种方法",程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念.多态(polymorphisn),字面意思多种形状. C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写.(这里我觉得要补充,重

  • C++面向对象之多态的实现和应用详解

    前言 本文主要给大家介绍的是关于C++面向对象之多态的实现和应用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 多态 大家应该都听过C++三大特性之一多态,那么什么多态呢?多态有什么用?通俗一点来讲-> 多态性可以简单地概括为"一个接口,多种方法",程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念.当多态应用形参类型的时候,可以接受更多的类型.当多态用于返回值类型的时候,可以返回更多类型的数据.多态可以让你的代码拥有更好的扩展性. 多态分

  • C++之多态(内容不错)

    编译环境:WIN10 VS2017 这篇博客有点长,但都是满满的干货,一定要看到最后,那才是重点. 什么是多态? 顾名思义就是同一个事物在不同场景下的多种形态. 下面会具体的详细的介绍. 静态多态 我们以前说过的函数重载就是一个简单的静态多态 int Add(int left, int right) { return left + right; } double Add(double left, int right) { return left + right; } int main() { A

  • C++多态的实现及原理详细解析

    1. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数.2. 存在虚函数的类都有一个一维的虚函数表叫做虚表.类的对象有一个指向虚表开始的虚指针.虚表是和类对应的,虚表指针是和对象对应的.3. 多态性是一个接口多种实现,是面向对象的核心.分为类的多态性和函数的多态性.4. 多态用虚函数来实现,结合动态绑定.5. 纯虚函数是虚函数再加上= 0.6. 抽象类是指包括至少一个纯虚函数的类. 纯虚函数:virtual void breathe()=0:即抽象类!必须在子类实现这个函数!

  • C语言模式实现C++继承和多态的实例代码

    这个问题主要考察的是C和C++的区别,以及C++中继承和多态的概念. C和C++的区别 C语言是面向过程的语言,而C++是面向对象的过程. 什么是面向对象和面向过程? 面向过程就是分析解决问题的步骤,然后用函数把这些步骤一步一步的进行实现,在使用的时候进行一一调用就行了,注重的是对于过程的分析.面向对象则是把构成问题的事进行分成各个对象,建立对象的目的也不仅仅是完成这一个个步骤,而是描述各个问题在解决的过程中所发生的行为. 面向对象和面向过程的区别? 面向过程的设计方法采用函数来描述数据的操作,

随机推荐