面向对象程序设计基于三个基本概念:数据抽象,继承和动态绑定

数据抽象是一种依赖于接口和实现分离的编程技术。继承和动态绑定对程序的编号有两方面的影响:一是我们可以更容易地定义与其它类相似但不完全相同的类;二是在使用这些彼此相似的类编写程序时,我们可以在一定程度上忽略掉它们的区别。

在 c++ 语言中,当我们使用基类的引用或指针调用一个虚函数时将发生动态绑定

定义基类:

 class Quote {
public:
Quote() = default;
Quote(const std::string &book, double sales_price) :
bookNo(book), price(sales_price) {} std::string isbn() const {
return bookNo;
} virtual double net_price(std::size_t n) const {//定义成虚函数,运行2时进行动态绑定
return n * price;
} virtual ~Quote() = default;//基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作 private:
std::string bookNo;//书籍的isbn编号 protected://可被派生类访问
double price = 0.0;//代表普通状态下不打折的价格
};

注意:基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作

基类通过在其成员函数的声明之前加上关键字 virtual 使得该函数执行动态绑定。任何构造函数之外的非静态函数都可以是虚函数。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数

派生类可以继承定义在基类中的成员,但是派生类不一定有权访问从基类继承而来的成员。派生类只能访问公有成员和受保护的成员,不能访问私有成员

定义派生类: 

 class Bulk_quote : public Quote {
public:
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t, double); double net_price(std::size_t) const override;//override显式注明该成员函数覆盖它继承的虚函数 // ~Bulk_quote(); private:
std::size_t min_qty = ;//适用折扣政策的最低购买量
double discount = 0.0;//以小数表示的折扣额 };

注意:override 显式注明该成员函数覆盖它继承的虚函数(只能对继承自虚函数的成员使用 override 关键字)

派生类对象及派生类向基类的类型转换:

因为在派生类对象中含有与其基类对应的组成部分,所以我们能把派生类的对象当作基类对象使用,而且我们也能将基类的指针或引用绑定到派生类对象中的基类部分上:

     Quote item;//基类对象
Bulk_quote bulk;//派生类对象 Quote *p = &item;//p指向Quote对象
p = &bulk;//p指向bulk中的Quote部分 Quote &r = bulk;//r绑定到bulk中的Quote部分

注意:这种转换通常称为派生类到基类的类型转换。和其它类型转换一样,编译器会隐式地执行派生类到基类的转换。这种隐式特性意味着我们可以把派生类对象或者派生类对象的引用用在需要基类引用的地方;同样的,我们可以把派生类对象的指针用在需要基类指针的地方

派生类构造函数:

尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些成员。和其它创建了基类对象的代码一样,派生类也必须使用基类的构造函数类初始化它的基类部分

 Bulk_quote::Bulk_quote(const std::string &book, double p, std::size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {}

注意:首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员

派生类中基类数据成员如果没有显式构造,则会执行默认初始化

派生类使用基类的成员:

 double Bulk_quote::net_price(size_t cnt) const {
if(cnt >= min_qty) return cnt * ( - discount) * price;
return cnt * price;
}

注意:派生类可以访问基类的公有成员和受保护成员

派生类的作用域嵌套在基类的作用域之内。因此,对于派生类的一个成员来说,它使用派生类成员的方式与使用基类成员的方式是一样的

继承与静态成员:

 #include <iostream>
using namespace std; class Base{
public:
// Base();
// ~Base();
static void statmem(); }; void Base::statmem() {
//
} class Derived : public Base{
public:
// Derived();
// ~Derived();
void f(const Derived&);
}; void Derived::f(const Derived &derived_obj) {
Base::statmem();//正确,Base定义了stamem
Derived::statmem();//正确,Derived继承了stamem
derived_obj.statmem();//通过Derived对象访问
statmem();//通过this对象访问
} int main(void){ }

注意:如果基类定义了一个静态成员,则在整个继承体些中只存在该成员的唯一定义。无论从基类中派生出多少个派生类,对于每个静态成员来说都只存在唯一的实例

静态成员遵循通用的访问控制规制,如果基类中的成员是 private 的,则派生类无权访问它

派生类的声明:

派生类的声明中包含类名但不包含派生列表:

 class Bulk_quote : public Quote;//错误,派生列表不能出现在这里
class Bulk_quote;//正确

被用作基类的类:

如果我们想将某个类用作基类,则该类必须已经定义而非仅仅声明:

 class Quote; //声明但未定义
class Bulk_quote : public Quote {//错误,Quote必须被定义
//
}

一个类是基类,同时也可以是一个派生类。最终的派生类将包含它的直接基类的子对象以及每个简介基类的子对象

防止继承的发生:

在类名后面跟一个关键字 final 能防止继承发生:

 class NoDerived final {};//NoDerived不能作为基类
class Base {}; class Last final : Base {};//Last是final的,我们不能继承Last // class Bad : NoDerived {};//错误,NoDerived是final的
// class Bad2 : Last {};//错误,Last是final的

类型转换与继承:

可以将基类的指针或引用绑定到派生类对象上,当使用基类的引用或指针时,实际上我们并不清楚该引用或指针所绑定对象的真实类型。该对象可能是基类的对象,也可能是派生类的对象

和内置指针一样,只能指针类也支持派生类想基类的类型转换,这意味着我们可以将一个派生类对象的指针存储在一个基类的智能指针内

静态类型与动态类型:

当使用存在继承关系的类型时,必须将一个变量或其它表达式的静态类型与该表达式表示对象的动态类型区分开来。表达式的静态类型在编译时总是已知的,它是变量声明时类型或表达式生成的类型;动态类型则是变量或表达式的内存中对象的类型。动态类型直到运行时才可知。

如果达式既不是引用也不是指针,则它的动态类型永远与静态类型一致。而基类的指针或引用的静态类型可能与其动态类型不一致。如:

     Bulk_quote bulk;//派生类对象
Quote q* = &bulk;//q的静态类型为Quote,动态类型为Bulk_quote
Quote &p = bulk;//p的静态类型为Quote,动态类型为Bulk_quote

不存在从基类向派生类的隐式类型转换:

之所以存在派生类向基类的类型转换是因为每个派生类对象都包含一个基类部分,而基类的引用或指针可以绑定到该基类部分上。但基类不一定包含派生类(派生类可能定义了新的成员),所以如果我们将一个基类的对象向派生类的类型转换,则我们有可能会使用基类中没有的成员,所以不存在从基类向派生类的自动类型转换

     Quote base;
// Bulk_quote *bulkp = &base;//错误,不能将基类转换成派生类
// Bulk_quote &bulkref = base;//错误,不能将基类转换成派生类 // 即使一个基类指针或引用绑定在一个派生类对象上,也不能执行从基类向派生类的转换
Bulk_quote bulk;
Quote *itemp = &bulk;//正确,从派生类转换到基类
// Bulk_quote *bulkp = itemp;//错误,不能将基类转换成派生类

注意:即使一个基类指针或引用绑定在一个派生类对象上,也不能执行从基类向派生类的转换

我们可以通过 dynamic_cast 或 static_cast 显式地将一个基类对象转换为派生类类型。详见百度百科:https://baike.baidu.com/item/dynamic_cast/4473047?fr=aladdin

在对象之间不存在类型转换:

派生类向基类的自动类型转换只对指针或引用类型有效,在派生类类型和基类类型之间不存在这样的转换。但我们可以通过拷贝构造函数、移动构造函数、拷贝赋值运算符或移动赋值运算符将一个派生类类型转换成基类类型,因为这些拷贝控制成员中通常包含一个本类类型的 const 引用或右值引用:

 #include <iostream>
using namespace std; class Quote {
public:
Quote() = default;
Quote(const std::string &book, double sales_price) :
bookNo(book), price(sales_price) {} std::string isbn() const {
return bookNo;
} virtual double net_price(std::size_t n) const {//定义成虚函数,运行2时进行动态绑定
return n * price;
} virtual ~Quote() = default;//基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作 private:
std::string bookNo;//书籍的isbn编号 protected://可被派生类访问
double price = 0.0;//代表普通状态下不打折的价格
}; class Bulk_quote : public Quote {
public:
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t, double); double net_price(std::size_t) const override;//override显式注明该成员函数覆盖它继承的虚函数 // ~Bulk_quote(); private:
std::size_t min_qty = ;//适用折扣政策的最低购买量
double discount = 0.0;//以小数表示的折扣额 }; Bulk_quote::Bulk_quote(const std::string &book, double p, std::size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {} double Bulk_quote::net_price(size_t cnt) const {
if(cnt >= min_qty) return cnt * ( - discount) * price;
return cnt * price;
} int main(void){ Bulk_quote bulk;//派生类对象
Quote item(bulk);//使用合成拷贝构造函数Quote::Quote(const Quote&)
item = bulk;//使用合成拷贝赋值运算符Quote& Quote::operator=(const Quote&) //显然我们不能将基类类型通过拷贝控制成员转换成派生类对象
// Quote cnt;
// Bulk_quote gg(cnt); return ;
}

注意:在上述过程中会忽略 Bulk_quote 中新定义的成员,即 bulk 中只有从基类中继承来的部分被赋值给了 item

显然,我们不能将基类类型通过拷贝控制成员转换成派生类对象

OOP1(定义基类和派生类)的更多相关文章

  1. C++学习21 基类和派生类的赋值

    在C/C++中,经常会发生数据类型转换,例如整型数据可以赋值给浮点型变量,在赋值之前,先把整型数据转换为浮点型:反过来,浮点型数据也可以赋值给整型变量. 数据类型转换的前提是,编译器知道如何对数据进行 ...

  2. C++:基类与派生类对象之间的赋值兼容关系

    4.5 基类与派生类对象之间的赋值兼容关系 在一定条件下,不同类型的数据之间可以进行类型转换,例如可以将整型数据赋给双精度型变量. 在赋值之前,先把整型数据转换为双精度型数据,然后再把它双精度型变量. ...

  3. C++:基类和派生类

    4.1 派生类的声明 继承实例如下: class Person{ //声明基类Person public: void print() { cout<<"name:"&l ...

  4. Java基类和派生类

    背景:对基类和派生类有更清晰的认识. 从外部看来,派生类是一个与基类具有相同接口的新类,或许还会有一些额外的的方法和域 .但继承并不仅仅是类的复用.当创建了一个派生类的对象时,该类包含了一个基类的子对 ...

  5. 详解C++中基类与派生类的转换以及虚基类

    很详细!转载链接 C++基类与派生类的转换在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中 ...

  6. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

  7. (转) C++中基类和派生类之间的同名函数的重载问题

    下面有关派生类与基类中存在同名函数 fn: class A { public: void fn() {} void fn(int a) {} }; class B : public A { publi ...

  8. c++中基类与派生类中隐含的this指针的分析

    先不要看结果,看一下你是否真正了解了this指针? #include<iostream> using namespace std; class Parent{ public: int x; ...

  9. 基类和派生类--this

    基类指针在程序运行的时候的确指向的是一个派生类的对象,但指针的类型仍然是基类指针.C++是一种强类型语言,因此不能用基类指针类型的指针直接调用派生类:而且,同一个类可能有多种不同的派生类,因此不知道实 ...

随机推荐

  1. oracle 11g 导出空表

    正常情况下,oracle11g的 exp命令无法导出空表,弥补这个缺陷的方法是 在空表创建之前,更改系统设置: show parameter deferred_segment_creation 查看, ...

  2. Mybites和hibernate的优缺点和区别

    Hibernate 是当前最流行的O/R mapping框架,它出身于sf.net,现在已经成为Jboss的一部分. Mybatis 是另外一种优秀的O/R mapping框架.目前属于apache的 ...

  3. temp8

  4. mysql语句规范

  5. xcode添加build phase

    [xcode添加build phase] xcode版本:5.0.2,找了半天,终于找到add build phase的方法,如下图.

  6. SpringMVC 课程第一天

    SpringMVC第一天   框架课程 1. 课程计划 第一天 1.SpringMVC介绍 2.入门程序 3.SpringMVC架构讲解 a) 框架结构 b) 组件说明 4.SpringMVC整合My ...

  7. 安装 SQL Server 2014 Express

    安装 SQL Server 2014 Express 我的电脑系统: Windows 10 64位 一 . 下载 安装Microsoft SQL Server 2014 Express 软甲下载地址: ...

  8. 在Ubuntu安装Tomcat7.0及开机自动运行

    在Ubuntu安装Tomcat7.0及开机自动运行 1.安装装Tomcat7.0 一般都是绿色版的,下载一个tomcat7.0解开到指定的目录上即可 然后进入tomcat目录的bin文件夹,执行 su ...

  9. GNU 和 g++(转)

    百度知道 GNU计划,又称革奴计划,是由Richard Stallman在1983年9月27日公开发起的.它的目标是创建一套完全自由的操作系统.Richard Stallman最早是在net.unix ...

  10. CentOS7下源码包方式安装rabbitmq

    1.先安装erlang http://www.cnblogs.com/justphp/p/6093880.html 2.下载rabbitmq rpm包: wget http://www.rabbitm ...