//############################################################################
/*
* 公有,保护,私有继承
*/ class B {
};
class D_priv : private B { }; //私有继承
class D_prot : protected B { }; //保护继承
class D_pub : public B { }; //公有继承 /*
不同的继承方法指定了派生类对基类不同的访问控制权限 上述三个派生类:
1. 都不能访问B中的私有成员.
2. D_pub继承B中的公有成员为公有,继承B中的保护成员为保护
3. D_priv继承B中的公有保护成员为私有
4. D_prot继承B中的公有保护成员为保护 转换:
1. 任何人都可以将一个D_pub*转换为B*. D_pub是B的特殊情况
2. D_priv的成员和友元可以将D_priv*转成B*
3. D_prot的成员,友元及子女可以将D_prot*转成B* 注意:只有公有继承是is-a的关系
*/ /* 详细实例 */ class B {
public:
void f_pub() { cout << "f_pub is called.\n"; }
protected:
void f_prot() { cout << "f_prot is called.\n"; }
private:
void f_priv() { cout << "f_priv is called.\n"; }
}; class D_pub : public B { //对于公有继承
public:
void f() {
f_pub(); // OK. D_pub的公有成员函数
f_prot(); // OK. D_pub的保护成员函数
f_priv(); // Error. B的私有成员函数
}
}; class D_priv : private B { //对于私有继承
public:
using B::f_pub; //使其在D_priv的作用域内可见
void f() {
f_pub(); // OK. D_priv的私有成员函数
f_prot(); // OK. D_priv 的私有成员函数
f_priv(); // Error. B的私有成员函数
}
}; int main() {
D_pub D1;
D1.f_pub(); // OK. f_pub()是D_pub的公有成员函数 D_priv D2;
D2.f_pub(); // Error. f_pub()是D_priv的私有成员函数,增加using B::f_pub之后OK B* pB = &D1; // OK 可以转换
pB = &D2; // Error 不能转换
...
} //############################################################################
/*
* 私有继承: 类似于has-a关系,跟组合类似
*/
class Ring {
virtual tremble();
public:
void tinkle() {...; tremble(); }
}; /* 组合 */
class Dog {
Ring m_ring;
public:
void tinkle() {m_ring.tinkle();} // 向前调用
}; /* 私有继承*/
class Dog_Priv : private Ring {
public:
using Ring::tinkle;
}; /*
* 组合的优点: 比如可以有多个ring,ring可以切换 //通常情况下倾向于组合,更低耦合,更灵活
* 私有继承的优点:更优雅的多态,比如
*
* 在ring类中增加虚函数tremble(), 该函数在tinkle中被调用
*/ /*
* 公有继承 => "is-a" 关系
*
* 基类能做的任何事情,派生类需要也能做
*/ //像以下类的设计就是不合适的
class Bird {
public:
void fly();
}; class Penguin : public Bird {}; Penguin p;
p.fly(); // class flyableBird : public bird {};
// public:
// void fly();
//penguin p;
//p.fly(); // 看几个例子
class Dog {
public:
void bark() { cout << "Whoof, I am just a dog.\n";};
}; class Yellowdog : public Dog{
public:
void bark() { cout << "Whoof, I am a yellow dog.\n";};
}; int main() {
Yellowdog* py = new Yellowdog();
py->bark();
Dog* pd = py;
pd->bark();
} OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog.
/*
* 结论:不要覆写非虚函数
*/ class Dog {
public:
void bark(int age) { cout << "I am " << age; }
virtual void bark(string msg = "just a" ) {
cout << "Whoof, I am " << msg << " dog." << endl; }
}; class Yellowdog : public Dog {
public:
using Dog::bark;
virtual void bark(string msg="a yellow" ) {
cout << "Whoof, I am " << msg << " dog." << endl; }
}; int main() {
Yellowdog* py = new Yellowdog();
py->bark(5);
} OUTPUT:
Whoof, I am a yellow dog.
Whoof, I am just a dog. /*
* 不要重新定义虚函数的默认参数
* - 默认参数是静态绑定的
* - 虚函数是动态绑定的
*/ /*
* 在类dog中增加如下函数:
* virtual void bark(int age) { cout << "I am " << age << " years old"<< endl; }
* in main(),
* py->bark(5); // 编译不过
* // 可以通过在yellowdog中加"using Dog::bark;"修复 为了保持is-a关系
*/ // 防止意外覆写,或者没有覆写,增加了override关键字
class Dog {
public:
virtual void bark() { cout << "I am just a dog.\n";};
void run();
}; class Yellowdog : public Dog{
public:
virtual void barks() { cout << "I am a yellow dog.\n";}; //在旧标准中不会报错,等出错了调试比较困难
}; // C++ 11 standard:
class Yellowdog : public Dog{
public:
virtual void barks() override;
// 编译错误:没有覆写的函数 virtual void bark() const override;
// 编译错误:没有覆写的函数 void run() override; // 压根不是虚函数,错误
}; /*
* 避免采坑:
* 1. 类的精确定义
* 2. 不要覆写非虚函数
* 3. 不要覆写虚函数的默认参数
* 4. 强制继承被遮盖的函数
* 5. 小心函数覆写时的错字
*/

C++进阶--类的继承的更多相关文章

  1. Python类的继承(进阶5)

    转载请标明出处: http://www.cnblogs.com/why168888/p/6411918.html 本文出自:[Edwin博客园] Python类的继承(进阶5) 1. python中什 ...

  2. 苹果新的编程语言 Swift 语言进阶(十)--类的继承

    一.类的继承 类能够从其它类继承方法.属性以及其它特性,当一个类从另外的类继承时,继承的类称为子类,它继承的类称为超类.在Swift中,继承是类区别与其它类型(结构.枚举)的基础行为. 1.1 .类的 ...

  3. Java+7入门经典 - 6 扩展类与继承 Part 1/2

    第6章 扩展类与继承 面向对象编程的一个重要特性: 允许基于已定义的类创建新的类; 6.1 使用已有的类 派生 derivation, 派生类 derived class, 直接子类 direct s ...

  4. Javascript 进阶 面向对象编程 继承的一个样例

    Javascript的难点就是面向对象编程,上一篇介绍了Javascript的两种继承方式:Javascript 进阶 继承.这篇使用一个样例来展示js怎样面向对象编程.以及怎样基于类实现继承. 1. ...

  5. Javascript 进阶 面向对象编程 继承的一个例子

    Javascript的难点就是面向对象编程,上一篇介绍了Javascript的两种继承方式:Javascript 进阶 继承,这篇使用一个例子来展示js如何面向对象编程,以及如何基于类实现继承. 1. ...

  6. 游戏编程之Unity常用脚本类的继承关系

    前言学习Unity开发引擎的初学者会接触大量的脚本类,而这些类之间的关系往往容易被忽略.本文对Unity引擎开发中的一些常用类及其关系进行了简单的归纳总结. 博文首发地址:http://tieba.b ...

  7. Objective-C编程 — 类和继承

    讲述面向对象中的一个重要概念——继承,使用继承 可以方便地在已有类的基础上进行扩展,定义一个具有父 类全部功能的新类. 父类和子类 我们在定义一个新类的时候,经常会遇到要定义的新类是某个类的扩展或者是 ...

  8. C++学习笔记:07 类的继承与派生

    课程<C++语言程序设计进阶>清华大学 郑莉老师) 基本概念 继承与派生的区别: 继承:保持已有类的特性而构造新类的过程称为继承. 派生:在已有类的基础上新增自己的特性(函数方法.数据成员 ...

  9. UML类图(上):类、继承和实现

    面向对象设计 对于一个程序员来说,在工作的开始阶段通常都是别人把东西设计好,你来做.伴随着个人的成长,这个过程将慢慢变成自己设计一部分功能来实现,自己实现.如果要自己设计,无论是给自己看,还是给别人看 ...

随机推荐

  1. 百练8216-分段函数-2016正式A题

    百练 / 2016计算机学科夏令营上机考试 已经结束 题目 排名 状态 统计 提问   A:分段函数 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 编写程序 ...

  2. SharpZipLib 压缩ZIP导出

    var uploadSectionDir = Path.Combine("Upload", "QQ", DateTime.Now.ToString(" ...

  3. 不输入密码执行sudo 命令

    命令行执行的crontab 命令,但是需要包含sudo 才可以执行的命令,怎么办呢?见下: leo@leo-Ubuntu:/etc$ visudovisudo: /etc/sudoers: 权限不够v ...

  4. Go Example--超时处理

    package main import ( "fmt" "time" ) func main() { c1 := make(chan string, 1) go ...

  5. mongodb 通过mongodump来备份Sharded Cluste分片集群

    1,mongodb所有组件官方文档地址:https://docs.mongodb.com/manual/reference/command/,所有的基础组件都在里面,包括备份恢复的mongodump. ...

  6. C++中的抽象类

    一.抽象类学习笔记 1.virtual修饰函数(虚函数)后面加=0就称为一个纯虚函数,一个类中只要有纯虚函数那么它就是一个抽象类.抽象类不能用来实例化对象,是用来给他的派生类定义好这些框架的,给使用这 ...

  7. 05C++引用

    1.变量名 变量名实质上是一段连续存储空间的别名,是一个标号(门牌号): 通过变量来申请并命名内存空间: 通过变量的名字可以使用内存空间. 2.引用的概念 变量名,本身是一段内存的引用,即别名(ali ...

  8. DevExpress 控件使用菜单栏之BarManager

    DevExpress 开发的控件有很强的实力,不仅功能丰富,应用简便,而且界面华丽,更可方便定制.对于编程人员来说是个不错的选择.它的菜单栏控件更具代表,完全可以替代开发环境提供的基本控件,而让您编写 ...

  9. Bat相关的项目应用

    原 bat 命令如何启动远程PC上的一个程序? 原 bat 自动解压缩,发布asp.net程序 原 bat 自动更新代码,编译,压缩asp.net程序 原 bat自动备份压缩文件 原 bat命令ora ...

  10. count(*) 和 count(1)和count(列名)区别

    执行效果上:  count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL  count(1)包括了所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL  cou ...