重学C++ (十一) OOP面向对象编程(2)

转换与继承

本节主要须要区分的是:

  • 基类和派生类的转换;
  • 引用(指针)的转换和对象的转换。

1.每一个派生类对象包括一个基类部分。因此。能够像使用基类对象一样在派生类对象上执行操作。

基于这一点,能够将派生类对象的引用(指针)转换为基类子对象的引用(指针),且存在自己主动转换。

反之,基类到派生类的自己主动转换是不存在的,因此基类不包括派生类型的成员。另外,将基类指针或引用绑定到派生类对象时也存在限制,由于编译器编译时无法知道该转换是安全的(编译器确定转换是否合法,仅仅看指针或引用的静态类型)。假设我们确定基类到派生类的转换是安全的,能够使用static_cast强制编译器进行转换,也能够用dynamic_cast申请在执行时进行检查。

2.引用转换不同于对象转换。

在引用转换中。对象本身未被复制,转换不会在不论什么方面改变派生类型对象。

在对象转换中(不是引用或指针),形參类型是固定的。将派生类对象传给基类对象形參时,该派生类对象的基类部分被【复制】到形參,这里是对基类对象进行初始化或赋值:初始化时调用构造函数,赋值时调用赋值操作符。(其实,一般基类的复制构造函数和赋值操作符的形參是基类类型的const引用,由于存在从派生类引用到基类引用的转换,故这两个成员函数可用于从派生类对象到基类对象进行初始化或赋值)。

构造函数和复制控制

构造函数

样例1:

class Bulk_item: public Item_base
{
public:
    Bulk_item():min_qty(0), discount(0.0) {}
};

该构造函数隐式调用基类的默认构造函数初始化对象的基类部分:首先使用Item_base的默认构造函数初始化Item_base部分,之后再初始化Bulk_item部分的成员,并执行构造函数的函数体。

样例2:

class Bulk_item: public Item_base
{
public:
    Bulk_item(const std::string& book, double sales_price, std::size_t qty=0, double disc_rate=0.0):
    Item_base(book, sales_price),min_qty(qty), discount(disc_rate) {}
};

该构造函数使用有两个形參的Item_base构造函数初始化基类子对象。

*首先初始化基类。然后依据【声明次序】初始化派生类的成员。

*一个类仅仅能初始化自己的【直接】基类。

复制控制和继承

*仅仅包括类类型或内置类型数据成员,不含指针的类一般能够使用合成操作。复制、复制或撤销这种成员不须要特殊控制。

假设派生类定义了自己的复制构造函数,该复制构造函数一般应【显式】使用基类复制构造函数初始化对象的基类部分:

class Base {/* …… */};
class Derived: public Base
{
public:
    //Base::Base(const Base&) not invoked automatically
    Derived(const Dervied& d):
        Base(d) /* other member initalization*/
    {/* …… */}
};

Base(d)将派生类对象d转换为它的基类部分的引用,并调用基类复制构造函数。

*假设忽略Base(d)。则将执行Base的【默认】构造函数初始化对象的基类部分——并不符合我们【复制】的本意。

*对于复制操作符。必须防止自身赋值。

Derived &Derived::oprator=(const Derived &rhs)
{
    if (this != &rhs)
    {
        Base::operator=(rhs); //assign the base part
        //assign the derived part
    }
    return *this;
}

派生类析构函数不负责撤销基类对象的成员。【编译器】总是【显式】调用派生类对象基类部分的析构函数。每一个析构函数仅仅负责清楚自己的成员:

class Derived: public Base
{
public:
    //Base::~Base() invoked automatically
    ~Derived() {/* 清理派生类部分的成员 */}
};

*对象的撤销顺序与构造顺序相反:先执行派生类析构函数。然后按继承层次依次向上调用个基类析构函数。

虚析构函数

要保证执行适当的析构函数。基类中的析构函数必须为虚函数:

class Item_base
{
    virtual ~Item_base() {}
};

Item_base *itemP = new Item_base;
delete itemP; //调用Item_base的析构函数
itemP = new Bulk_item; //指针的静态类型和动态类型不同
delete itemP; //调用Bulk_item的析构函数

在上述情况中。假设我们不定义虚析构函数。则第二个delete调用的将是Item_base的析构函数,这对于派生类型是没有定义的行为。

*构造函数不能定义为虚函数。赋值操作符设为虚函数则可能会令人混淆,并且不会有什么用处。

构造函数和析构函数中的虚函数

首先须要明白:

构造派生类对象时先执行基类构造函数,此时对象的派生类部分是未初始化的。

撤销派生类对象时先撤销派生类部分。

在这两种情况下,对象都是不完整的。

因此,在基类构造函数或析构函数中,编译器将派生类对象【以基类类型对象对待】。

假设在构造函数或析构函数中调用虚函数。执行的是构造函数或析构函数自身类型定义的版本号。

理由:

假设从基类构造函数中调用虚函数的派生类版本号,则派生类版本号可能会调用派生类成员。而此时对象的派生部分成员还未初始化!

该訪问将可能导致程序崩溃。

继承情况下的类作用域

在继承情况下,派生类的作用域嵌套在基类作用域中。

假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。

与基类成员同名的派生类成员将屏蔽对基类成员的直接訪问(可通过作用域操作符訪问被屏蔽的基类成员)。

注意。对于函数。即使函数原型不同。仅仅要名字相同。基类成员就会被屏蔽。

局部作用域中声明的函数不会重载全局作用域中定义的函数,相同,派生类中定义的函数也不重载基类中定义的成员。

因此,假设基类有下面函数:

int memfcn();

而派生类有下面函数:

int memfcn(int);

则对派生类调用memfcn()将报错。因此不存在对基类函数的重载,基类中的memfcn()被屏蔽。

这也解释了虚函数为什么必须在基类和派生类中拥有同一原型。

看下面代码:

class Base
{
public:
    virtual int fcn();
};

class D1: public Base
{
public:
    int fcn(int); //屏蔽基类的fcn()
};

class D2: public D1
{
public:
    int fcn(int); //非虚函数,屏蔽D1的fcn(int)
    int fcn(); //虚函数,重写基类的fcn()
};

Base bobj;
D1 d1obj;
D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); //call Base::fcn at run time
bp2->fcn(); //call Base::fcn at run time
bp3->fcn(); //call D2::fcn at run time

三个指针都是基类类型的指针,故通过在Base中查找fcn确定这三个调用。

由于fcn是虚函数,所以编译器会生成代码,在执行时基于引用或指针所绑定的实际类型进行调用。

纯虚函数

在函数形參表后面写上=0指定纯虚函数:

double fcn(std::size_t) const = 0;

含有一个或多个纯虚函数的类是抽象基类,除了作为抽象基类的派生类的对象的组成部分。不能创建抽象类型的对象。

容器与继承

假设一个容器存放的是基类类型的对象,当插入一个派生类对象时,会将派生类对象的基类部分【复制】到基类对象并保存在容器中(因此派生类部分将被切掉。也就是说容器里的仅仅能是基类对象,而不是派生类对象)。

一个解决方式是,使用容器保存对象的指针。

时间: 2024-08-03 19:02:29

重学C++ (十一) OOP面向对象编程(2)的相关文章

微软C#6.0语言OOP面向对象编程》《微软ASP.NET\MVC56网站开发》《微软Web服务\WCF\Web API3.0课程视频》

最新<微软C#6.0语言OOP面向对象编程><微软ASP.NET\MVC56网站开发><微软Web服务\WCF\Web API3.0课程视频>下载 下载链接:http://pan.baidu.com/s/1bnjJQnd 密码:36ji 微软与开源实战训练营QQ群:  203822816微软MSDN俱乐部QQ群:  29754721,

简单理解OOP——面向对象编程

OOP:面向对象编程 一. 什么是OOP 面向对象编程:Object-oriented Programming,OOP,面向对象程序设计. 面向对象编程是一种计算机编程架构,他的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成. OOP使得程序更有重用性.灵活性和扩展性.OOP的核心思想是:封装.继承.多态(重点是类和对象). 不同于POP(面向过程编程)的以过程为中心的编程思想,面向对象编程的中心思想是通过调用对象来实现想要实现的目的. 面向对象的思想: 是一种更符合我们

JavaScript 精粹 基础 进阶(8)OOP面向对象编程(上)

转载请注明出处 原文连接 http://blog.huanghanlian.com/article/5b698f14b8ea642ea9213f50 面向对象编程,oop并不是针对与javascript,很多语言都实现了oop这样一个编程发法论,比如说java,c++,都是实现了oop的语言. 概念与继承 概念 面向对象程序设计(Object-oriented programming OOP)是一种程序设计范型,同时也是一种程序开发的方法.对象指的是类的实例,它将对象作为程序的基本单元,将程序和

一百零六、SAP的OOP面向对象编程,OO-ALV的简介

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构. 基本概念: 1.对象(Object)是一个现实实体的抽象.一个对象可被认为是一个把数据(属性)和程序(方法)封装在一起的实体,这个程序产生该对象的动作或对它接受到的外界信号的反应.这些对象操作有时称为方法.对象是个动态的概念,其中的属性反映了对象当前的状态. 2.类(Class)用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.每个对象是类的

OOP面向对象编程的六大原则(上)

名词解释: OOA:面向对象分析 A:Analyze OOD:面向对象设计D:Design OOP:面向对象编程P:Program OOP六大原则(上):对象单一职责.里式替换原则.迪米特法则.开闭原则 对象单一职责:我们设计的对象必须职责明确,不能让他成为万能类,这里的对象可以是模块.类库,程序集,不单单指类. 1.特点:最常见的.比如分层:Model.业务层.UI层.把具有相同行为的类.放到一个模块,这个就是模块职责明确. 2.好处:每个类所包含的行为,属性都属于本身,后续的维护更具有针对性

js 设计模式 oop 面向对象编程

最初我们写js代码的时候是这么写 function checkName(){ //验证姓名 } function checkEmail(){ //验证邮箱 } function checkPassword(){ //验证密码 } 这种方式会造成全局变量的严重污染,再过渡到 var checkObject = { checkName : function(){}; checkEmail: function(){}; checkPassword: funcion(){}; } //也可如此写 var

Python 的OOP 面向对象编程基础

Objects Sorts of Program Languages Machine Orinted 机器指令 汇编语言 Procedure Orinted 按照步骤和分支处理 C Object Orinted 多人.多部门写作 C++ Java Python Python 中一切皆对象,面向对象做得非常彻底 对象是数据和操作的封装 对象是独立的,但对象之间可以互相作用 目前OOP是最接近人类认知的编程范式. OOP 描述数据--Property Action--Method class col

边记边学PHP-(十三)面向对象编程3

2.3成员方法(函数) 我理解的成员方法就是去具体执行这个类某些特定的功能,或者说这个类能做的事情.它与类外的函数用起来没有什么区别,只不过是在类里面声明的.在使用时需要用实例的类对象来调用它. 同样,成员方法也可以被权限修饰符修饰,private.protected.public.当使用修饰符修饰之后他们的使用权限与成员属性的使用权限一样.如果不显示的使用权限修饰符,默认为public.一般成员方法声明成public,方便对象调用,去操作类里面的私有属性. 在这里我就不特别的举例了. 2.4

OOP 面向对象编程

面向对象 对象的概念是面向对象技术的核心.在现实世界里,我们面对的所有事物都是对象,如计算机.电视机.自行车等.在面向对象的程序设计中,对象是一个由信息及对信息进行处理的描述所组成的整体,是对现实世界的抽象. 面向对象的三个主要特性: 对象的行为:可以对对象施加哪些操作:如电视机的开.关.转换频道等. 对象的状态:当施加那些方法时,对象如何响应:如电视机的外形.尺寸.颜色等: 对象的标识:如何区分具有相同行为与状态的不同对象. 基本概念: 对象,所谓数据封装就是将一组数据与这组数据有关操作组装在