`
evasiu
  • 浏览: 168993 次
  • 性别: Icon_minigender_2
  • 来自: 广州
博客专栏
Fa47b089-e026-399c-b770-017349f619d5
TCP/IP详解卷一>阅读...
浏览量:12524
社区版块
存档分类
最新评论

c++ premier -- 面向对象编程

 
阅读更多

显然,我从前对面向对象编程的理解是有失偏颇的。我以前常常觉得,面向对象编程无非就是从整体的角度出发,定义一些对象,以及对象的操作,通过它们的协力合作完成一件事情。可是,这中间忽略了面向对象编程的一个关键思想,即——多态性。通过折腾了他最后提供的一个实例,算是对这一章节的东西有了些了解。但是在设计层面上,我觉得自己还没有办法想到那里去。
 
与多态性紧密相关的两个概念,一个是继承,一个是动态绑定。
一、继承
继承涉及基类和派生类,派生类拥有基类的所有成员,但是访问的级别或方式可能会有所改变。改变的方法有几种:
(1)访问标识符
对于public和private访问标识符我们并不陌生,public成员可以让其他对象访问,而private成员只能被该类访问。对于基类的派生类而言,他既不是基类,但是他与基类的关系显然又与其他的类不一样。引入了继承的概念后,就引入了一个新的访问标识符protected,派生类可以访问基类的protected成员,但其他类不能访问。必须注意的是,这种访问与类的public成员被其他类对象通过类实例的.或->访问是不一样的,派生类不能通过一个基类对象去访问基类的protected成员,然而派生类可以通过自身类型的对象去访问基类中protected的成员。用下面的例子来说可能比较好一些:

class Item_base{
	public:
		//public members
	private:
		//private memebers;
	protected:
		double price;
	};
	
class Bulk_item: public Item_base{
	public:
		void memfcn( const Bulk_item&, const Item_base& );
	private:
		//private members;
	};

void Bulk_item::memfcn( const Bulk_item& d, const Item_base &b ){
	double ret = price; //ok: use this.price
	ret = d.price; //ok: derived class can access base class's protected member
	ret = b.price; //error: no access to price from a base class in the derived class context
	}

 从上面的例子我们也可以看到,在声明Bulk_item为Item_base的派生类时,还在Item_base前面添加了public,这是Bulk_item对Item_base的继承方式的声明。派生类对基类的继承方式不会改变派生类访问基类的级别,但是改变了派生类从基类继承来的成员在派生类中的访问标识符。同样的,总共有三种继承方式:
public继承基类成员保持自己的访问级别,基类的public成员在派生类中仍然是public的,依此类推。
protected继承,基类的public和protected成员到了派生类中都变成protected的,而private成员仍为Private成员。
private继承使基类的所有成员到了派生类中都变成了private成员。
也就是说,继承方式其实改变的是其他类(包括派生类可能有的派生类)对派生类中基类成员的访问方式。public派生类继承了基类的接口,它具有与基类相同的接口,这种方式称为接口继承;private和protected成员相当于继承了基类的操作,但并不把它们开放给其他类用户,这些派生通常被称为实现继承。
(2)友元关系与继承
像其他类一样,基类或派生类可以使其他类或函数成为友元,友元可以访问private和protected数据。友元关系不能继承。基类的友元对派生类的成员没有特殊的访问权限。如果基类被授予友元,则只有基类具有特殊访问权限,该类的派生类不能访问授予基类友元关系的类。
(3)屏蔽
如果在派生类中定义了一个与基类成员同名的成员,基类的该成员在派生类中被屏蔽。这个成员包括成员函数与数据成员,派生类的成员函数也会屏蔽掉基类的数据成员。即使派生了定义了一个成员函数虽然与基类成员函数同名,但是形参列表不同,这时候如果在派生类中调用该函数而使用的是基类中定义的形参列表,也不会出现所谓的重载。因为编译器一旦在派生类的作用域内找到了同名函数,候选函数集就只会在派生类形成了。虚函数的重新定义与屏蔽不一样,声明函数为虚函数是为了实现动态绑定,这跟非虚函数由编译器确定调用哪个成员是不一样的。当然,可以通过在成员前面加上域访问符以声明使用的是基类的成员。

派生类与基类的转换
如果有一个派生类的对象,可以使用它的地址对基类类型的指针进行赋值或初始化,同样,可以使用派生类型的引用或对象初始化基类类型的引用。还可以使用派生类型的对象对基类对象进行赋值或初始化,但是这两者之间是有微妙的差别的。
把派生类对象的地址或引用赋值给基类类型的指针或引用时,引用或指针直接绑定到该对象,对象本身并未被复制,并且,转换不会在任何方面改变派生类型的对象。
另一方面,如果把一个派生类对象赋值给基类类型的对象,派生类会被阉割而成为一个实实在在的基类类型对象。用派生类对象对基类类型的对象进行赋值或初始化时,一般有两种可能,一种是基类显示定义了将派生类赋值给基类的含义,如通过一个接受派生类引用的构造函数。更大的可能是编译器用派生类中的继承自基类数据成员初始化一个基类对象。
没有从基类类型对派生类类型的自动转换,但是可以static_cast或dynamic_cast强制编译器进行转换,但是这是危险的。

基类的private成员是派生类所访问不到的,一开始我就觉得很奇怪,既然这样,派生类又如何去初始化基类的数据成员呢?原来,在派生类的构造函数中,会首先调用基类的构造函数,通过基类构造函数对基类中的数据成员进行初始化,然后再初始化属于派生类的数据成员。
继承对基类的构造函数和复制控制没有太大的影响,除了在确定提供哪些构造函数时,必须考虑一类新用户。像其他任意成员一样,构造函数可以是protected或private的。
由于初始化派生类的时候,首先必须调用基类的构造函数,这一行为对派生类的复制控制也产生了影响。派生类的复制构造函数必须首先用派生类初始化一个基类,而在派生类的赋值操作符定义中,也必须首先调用基类的赋值操作符。另外,根类的析构函数必须声明为虚函数,这一点跟动态绑定有关。派生类的构造函数和复制控制一般有如下结构:

class Base{
	public:
		//doesn't have default constructor
		Base( double d ):price(d){}
		//a base class is asked to have a virtual desctructor
		virtual ~Base(){ }
	private:
		double price;
	};

class Derived: public Base{
	public:
		//constructor, first call base class if it does not have a default constructor
		Derived( double d, int t ):Base(d), times(t){}
			
		//copy constructor, initialize the base part using derived part
		Derived( Derived& dd ):Base(dd), times(dd.times){ }
			
		//first call the operator=() of the base part
		Derived& operator=( const Derived& rhs ){
			if( this != &rhs ){
				Base::operator=(rhs);
				times = rhs.times;
			}
			return *this;
		}
		
		//it's not a base class, so destructor is not necessarilty required
	private:
		int times;
	};

 继承所主要考虑的,主要就是基类和派生类之间的关系,派生类继承了基类什么,这些继承来的成员是如何被派生类和其他派生类的类用户使用的,基类和派生类的转换,以有继承对类的复制控制的影响。

 

二、动态绑定
“在C++中,多态性仅用于对它继承而相关联的类型的引用或指针。”
现在我们知道,多态的一个重要方面是继承,只有通过继承,才可以互换地使用派生类型或基类型的许多形态,所谓的“许多形态”,则是通过动态绑定实现的。通过在基类声明函数为virtual的,派生类重新定义该virtual函数的实现,这样,整个继承层次就有了多态的特征。
引用和指针的静态类型与动态类型可以不同,这是C++用以支持多态性的基石。
通过基类引用或指针调用基类中定义的函数时,我们并不知道执行函数的对象的确切类型,执行函数的对象可能是基类类型,也可能是派生类类型。
如果调用非虚函数,则无论实际对象是什么类型,都执行基类类型所定义的函数。如果调用虚函数,则直到运行时才能确定调用哪个函数,运行的虚函数是引用所绑定的或指针所指向的对象所属类型定义的版本。
前面我们讲到如果把一个派生类型的对象赋值给基类类型的对象,派生类对象会被阉割成基类类型的对象,而使用指针和引用则不会。这样我们就明白了,与多态性相关联的关键字包括继承、虚函数、引用或指针。
现在我们也应该能够明白,为什么根类的析构函数必须是虚函数:
删除指向动态分配对象的指针时,需要运行析构函数在释放对象的内存之前消除对象。处理继承层次中的对象时,指针的静态类型可能与被删除对象的动态类型不同,可能会删除实际指向的派生类对象的基类类型指针。如果删除基类指针,则需要运行基类析构函数并清除基类的成员,如果对象实际是派生类型的,则没有定义该行为。通过把析构函数定义为虚函数,可以在运行时动态调用析构函数的正确版本。
虚函数也可以被重载。如果派生类中声明的函数跟基类的虚函数的形参不一样,则该函数是对应虚函数的重载版本,如果在派生类使用虚函数的形参调用该成员函数,则实际调用的是基类的成员函数。这是虚函数与非虚函数的区别:虚函数是重载,而非虚函数是屏蔽。
与虚函数相关的还有纯虚函数,通过在虚函数的形参列表后面加上=0使函数声明为纯虚函数,含有或继承一个或多个纯虚函数的类是抽象基类,除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。纯虚函数只是声明了函数,并没有实现,具体的实现要在继承该抽象基类的类中实现。

程序=设计+实现。以上部分主要都是设计实现时所需要理解的概念,并未涉及设计。书中最后还提供了两个小节,用于阐述句柄类的设计思路,以及一个简单的检索系统的多态设计与实现。虽然最终我都实现了这些,但是对于他的设计,我却有些望尘莫及。看完这本书,我希望可以进一步看有关设计方面的书吧。
 

分享到:
评论

相关推荐

    C++premier 第四版中文版

    C++是一种广泛使用的、面向对象的编程语言,由Bjarne Stroustrup在20世纪80年代初基于C语言开发而来。它在系统软件、游戏开发、高性能计算等领域有着重要的应用。 ### 标签中的知识点 #### C++ Premier 这个标签...

    C++ premier中文版 电子书

    C++是一种强大的、通用的面向对象编程语言,由Bjarne Stroustrup于1979年在贝尔实验室发展而来,它是基于C语言的扩展。C++不仅保留了C语言的效率和灵活性,还引入了类、模板、异常处理、命名空间等面向对象的概念,...

    C++ Premier第四版中文英文源代码

    源代码是学习编程语言的重要组成部分,通过阅读和实践书中的示例代码,读者可以深入理解C++的语法结构、编程思想以及面向对象编程的概念。这份"中文英文源代码"的提供,对于双语学习者来说尤为宝贵,既能够对照中文...

    c++ premier 第四版 课后习题答案+所有源代码

    《C++ Primer 第四版》的习题涵盖广泛,从基础语法到面向对象编程,再到STL(标准模板库)和泛型编程,都有所涉及。习题解答提供了正确答案和思路,有助于读者自我检查,理解错误之处,并学习如何解决实际问题。 源...

    C++ Premier 中英文对照chm版

    通过阅读《C++ Primer Plus》这本书,读者不仅可以掌握C++的基本语法,还能深入理解面向对象编程的理念,以及如何在实际项目中应用这些知识。CHM格式的电子书易于查找信息,便于在任何时间、任何地点进行学习。对于...

    C++Primer(中文版第4版)+源码+习题答案完整版.7z

    C++是一种静态类型的面向对象编程语言,因此在书中,作者会深入讲解类和对象的概念,包括封装、继承和多态性等面向对象特性。理解如何定义类、创建对象,以及如何通过成员函数和友元函数操作对象,是学习C++的关键。...

    [C..游戏编程入门].Premier.Press-Begining.C...Game.Programming.(2004).LRN.7.0-2.5.LotB

    - **面向对象编程**:介绍类与对象的概念,解释封装、继承和多态性等 OOP 特性。 #### 2. 游戏开发基础 - **游戏引擎与框架**:介绍常见的游戏开发工具和引擎,如 Unity、Unreal Engine 等,以及它们的基本使用...

    [BBservice]131.Teach_Yourself_ANSI_C++_in_21_Days.Premier_Edition.JL-JH.[EN]

    面向对象设计是C++的重要组成部分,没有良好的设计,C++可能只是一种稍加改进的C语言。这一章节将引导读者理解面向对象设计的基本原理,如何有效地组织代码,以及如何利用继承、多态等特性来构建更加灵活和可扩展的...

    [Premier]C++_Programming_For_The_Absolute_Beginner.zip

    C++是一种通用的、面向对象的编程语言,由Bjarne Stroustrup在C语言的基础上发展而来,它既具有静态类型的强类型系统,又支持过程化编程和面向对象编程。 在C++编程中,了解基本语法是入门的第一步。这包括变量定义...

    《C++ Primer》 要点总结.pdf

    C++语言是一种高级编程语言,支持面向对象编程、泛型编程和函数式编程。每个C++程序都包含一个或多个函数,其中一个必须命名为main。 2. 输入输出对象 C++语言提供了多种输入输出对象,包括cin、cout、cerr和clog...

    Game Programming All in One

    - **面向对象编程**:讲解类、对象、继承、多态等概念,以及它们在游戏开发中的应用。 - **内存管理**:探讨C++中的内存分配和释放机制,以及如何避免内存泄漏等问题。 - **性能优化**:分享提高游戏运行效率的技巧...

    使用Python Lua和Ruby语言进行游戏编程

    再者,Ruby语言以其“程序员的快乐”理念著称,它提供了一种更加面向对象的编程方式,适合快速开发。在游戏编程中,Ruby可能不如Python和Lua那么常见,但仍有其独特的应用场景。例如,Ruby可以用于构建游戏服务器,...

    IPL_Analyser_Cpp

    综上所述,IPL_Analyser_Cpp项目涉及了C++编程中的数据输入输出、数据结构与算法、面向对象编程、文件处理、异常处理等多个核心概念。通过这个项目,我们可以学习到如何利用C++进行数据分析和处理,以及如何构建一个...

Global site tag (gtag.js) - Google Analytics