C++ primer札记10-继承

包。继承,多态性C++的三个基本概念,在这里,我们重点总结继承的东西

1 类派生列表

类派生列表中指定一个派生类继承基类,来自列表与一个或多个基类如:

class B : public A1,protected A2,private A3

可是单继承时最常见的,多继承不多见

2 派生类的定义

派生类继承时,会包括父类的全部成员。即便私有成员不能被訪问。父类中的虚函数,在派生类中一般也要定义,如

果不定义的话。派生类将继承基类的虚函数

3 基类必须是已经定义的

一个只声明的类,是不能出如今派生类的类派生列表中。因此派生类非常可能要訪问基类的列表,可是假设基类还没

被定义,所以这样做是错误的。

4 派生类的声明

派生类的列表不能出如今派生类的列表中,如:

class B : public A; //error

正确的声明应是:

Class B;

Class A;

5 继承修饰符

C++中的继承方式有三种。public。protected,private继承(简称3p)。

无论是何种继承方式,基类中的private成员

都不能被派生的用户代码和成员定义代码訪问。接下来详细说说三种继承方式:

<1>private继承,基类的全部成员在派生类中都是private成员,可是基类的private成员依然不能被訪问

<2>protected继承,基类的public,protected成员变成子类的protected成员,基类的private成员依然不能被訪问

<3>public继承,基类的public变成派生类的public。protected变成派生类的protected。基类的private不能被訪问

6 protected 修饰符

当protected修饰类成员的时候,类成员能够被类自身成员定义所使用。可是不能被用户代码使用,这点类似private

其次,protected修饰的成员能够被子类訪问到。这点类似public,可是不用于private。

7 不能被继承的成员

构造函数,复制控制成员(复制构造函数,赋值操作符,析构函数)这是不能被继承的函数

8 派生类的构造函数

派生类构造函数一般格式:

DeriveClass(xxx):baseClass(xxx),x1(x),x2(x)....

<1> 派生类的构造函数除了要初始化自己新定义的成员外,还要初始化基类的成员。顺序是先调用基类的构造函数初始化基类的成员。然后再初始化自己的新成员(顺序是声明的顺序)

<2> 假设没有自定义构造函数。编译器会合成一个默认的构造函数,先调用基类的默认构造函数,然后就再去初始化其它新的成员。

<3> 这里须要注意的是:假设基类没有默认构造函数。那么编译器就不能合成默认构造函数,假设这个时候再不去定义派生类的默认构造函数,那样就会出错。诸如--“error C2512: “Derive”: 没有合适的默认构造函数可用。”

<4> 假设不在派生类构造函数的初始化列表中指定基类的构造函数,则会调用基类的默认构造函数

<5> 派生类构造函数的默认实參派生类能够将构造函数的全部參数都设置为默认參数,能够使用0-n个默认參数

9 派生类中的复制控制

复制控制包含复制构造函数,赋值操作符。析构函数

<1>未定义复制构造函数。编译器会自己合成一个复制构造函数,它会调用基类的复制构造函数

<2>自己定义复制构造函数,一般要调用基类的复制构造函数,否则会出意外

<3>未定义赋值操作符。编译会自己主动合成一个赋值操作符,它会调用基类的复制操作符

<4>自己定义赋值操作符要提防自身赋值。一般都是这么做

Derive& Derived::operator =(Derived& rh)
{
if(this != &rh)
{
Base::operator=(rh);
//do whatever needed to clean up the old value
//assign the members from the derived
}
}

这样做的原因就是为了防止自身赋值。在自身赋值的时候。我们通常要先把动态开辟的内存clear掉。假设不加上if(this != &rh)的话。那么在进行自身赋值的时候,就会把自己给废了(clear掉自己)。

<5> 派生类析构函数

这个和前两个不同,派生类的析构函数仅仅负责自己新定义对象的析构,不负责基类对象的析构。在派生类对象析构的时候,编译器会先调用派生类自己的析构函数,然后调用基类的析构函数。

每一个类无论基类还是派生类,都仅仅负责清除自己的成员。

以上的复制控制演示样例代码例如以下所看到的:

#pragma once
#include <iostream>
using namespace std;
class Base
{
public:
Base(){ m = 10;cout<<"Base default constructor"<<endl;}
Base(int x):m(x){}
Base(Base& rh){cout<<"the base copy constructor"<<endl;};
~Base(void){cout<<"Base destructor"<<endl;}
Base& operator = (Base& base){ this->m = base.m;cout<<"the base operator ="<<endl;return 

*this;}
private:
int m;
};

class Derive :
public Base
{
public:
~Derive(void){cout<<"Derived destructor"<<endl;}
private:
int mx;
};

void main()
{
Derive X;
cout<<"this is the separtor-----------------"<<endl;
Derive Y = X;
Y = X;
}

运行结果:

Base default constructor

this is the separtor-----------------

the base copy constructor

the base operator =

Derived destructor

Base destructor

Derived destructor

Base destructor

10 派生类函数的调用

在继承的情况下。派生类的作用域嵌套在基类的作用域中,假设不能再派生类中确定名字,就会在外围的基类中朝赵名字的定义。

<1>派生类调用函数是这种原则:先在派生类中查找名字,查到就停止。查不到的就在基类中查找。

<2>静态类型,动态类型

静态类型:是指不须要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态类型仅依赖于包括表达式的程序文本的形式,而在程序执行时不会改变

动态类型:由一个左值表达式表示的左值所引用的终于派生对象的类型。一个右值表达式的动态类型。就是它的静态类型。

<3>对象,引用或者指针的静态类型决定了对象可以运行的行为

因此引用或者指针不能运行派生类中新定义的成员名字。

<4>基类与派生类的名字发生冲突

基类与派生类中有同样的名字的时候。派生类会屏蔽基类中同名的成员。假设非要调用基类的成员,那么就必须显式的调用。如:Base::func();

<5>基类成员函数的屏蔽

基类和派生类有同样名字的函数,可是原型不一样(參数),那么派生类将不能直接调用基类的那个同名的函数。

依据之前的原则,找到名字同样的就不再往基类找了,所以以下的程序是错误的。

class A
{
public:
void fun(){};
}
class B : public A
{
public:
void fun(int){};
private int x;
}
B b;
b.fun(11); //OK
b.func();  //error

11 using

能够在派生类中使用using来改变继承自基类中的成员的级别。前提是派生类对基类的成员具有訪问权限。

//Base.h
#pragma once
#include <iostream>
using namespace std;
class Base
{
public:
Base(void){ n = 100; };
~Base(void){};
size_t size()const{return n;}
protected:
//private:
size_t n;
int fn(int x){return x;};
int fn(){return 11;}
};
//Derived.h
#pragma once
#include "base.h"
class Derived :
private Base
{
public:
Derived(void){};
~Derived(void){};
using Base::size;
using Base::fn;
};
//main.cpp
#include "Base.h"
#include "Derived.h"
#include <iostream>
using namespace std;

void main()
{
Derived XX;
Base YY;
cout<<XX.size()<<endl;
cout<<XX.fn()<<endl;
cout<<XX.fn(2)<<endl;
system("pause");
}

在这里我们也能够看到假设基类的函数有重载的话,针对同一个using Base::XXX,能够使所用的名字为XXX的基类函数在派生类中得到声明。

真可可谓一次声明,多个使用。

12 friend 跟继承的关系

友元跟继承没有关系。基类的派生类不能被基类的派生类訪问。

友元类的派生类对基类没有訪问权限。

13 引用转换,转换对象

引用转换:派生类对象转换为基类类型的引用

转换对象:用派生类对象转换为基类的对象。这个时候形參是固定的。编译和执行时候的对象都是基类类型对象。派生类的基类部分被拷贝到形參中。

引用转换:派生类对象转换为基类的引用。可是假设赋值的话也是把派生类的基类部分赋值给基类对象。这和指针的切片效应一个道理。

14 基类和派生类的转换

派生类转对象赋给基类也是有条件才干行的,这个条件就是派生类是通过public来继承的。这种话不论是在成员代码还是在用户代码中。都能实现

Derived d;
Base b = d;

假设是通过protected继承的,那么仅仅能在成员定义中使用这种代码。

15 默认的继承标准

类和结构在默认继承指引相反。

<1>当类继承,假设没有预选赛,那么默认为private继承。如果没有预选赛时的结构,它是默认public继承。

<2>此外strut从开始到内部的第一限定词之间定义的默认public修改。但class缺省值是private修改。

时间: 2024-10-08 20:04:22

C++ primer札记10-继承的相关文章

Android实习札记(10)---ImageView的src属性 VS blackground属性

Android实习札记(10)---ImageView的src属性 VS blackground属性                                                                              转载请注明出处:coder-pig 问题分析 相信大家对于ImageView图片组件并不陌生吧,见名知意,就是用来显示图片的咯! 而显示图片的话可以通过src属性,又或者blackground属性来进行设置! 这些大家都知道,但是有没有去纠结下

0721-----C++Primer听课笔记----------继承

1. 通过子类对象来调用函数: a)从父类继承而来的函数,可以正常使用: b)子类自行添加的函数,可以正常使用: c)子类编写和父类重名的函数,不管是否改变了参数,调用的都是子类的版本,如果需要调用父类的版本,需要显示指定父类名. 例1: #include <iostream> #include <string> using namespace std; /* * 派生类对象可以正常的调用从基类继承的函数 */ class Person{ public: Person(){} Pe

【重构学习】10 继承关系的重构

1.字段上移 修改点:两个子类拥有相同的字段 做法:将该字段移至父类 2.函数上移 修改点:有些函数,在各个子类中产生完全相同的效果 做法:将该函数移至父类 有一种特殊情况也需要这么做:子类函数覆盖了父类的,但是仍然做着相同的工作 在此重构中你可能会遇到一种情况,就是你提炼的函数调用了子类有而父类没有的函数,那么在父类中给此父类没有的函数,建立一个虚函数即可. 3.构造函数本体上移 修改点:你在各个子类中拥有一些构造函数,它们的本体几乎完全一致 做法:在父类中新建一个构造函数,并在子类构造函数中

c++ primer之10.1 泛型概述

在顺序容器中,没有定义可以满足用户(程序员)所需的更多功能接口,所以标准库定义了一组泛型算法,之所以称为"泛型的",适用于不同类型. 泛型算法一般不直接操作容器,而是遍历迭代器的元素范围来进行操作. 标准库算法find: 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 5 using namespace std; 6 7 int main() 8 { 9 cout

10.继承初步

面向对象的三个特征:继承,封装,多态 什么是继承? 一个类得到了另外一个类当中的成员方法和成员变量 Jvava当中支持单继承 为什么使用继承 减少类定义重复代码 继承的基本语法特点 继承之外,还可以扩展 classPerson{ String name; int age; void eat(){ System.out.println("吃饭"); } void introduce(){ System.out.println("我的名字是"+ name +"

【C++ Primer 第10章】再探迭代器

反向迭代器 • 反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器.对于反向迭代器,递增(以及递减)操作的含义会颠倒过来. • 递增一个反向迭代器(++it)会移动到前一个元素:递减一迭代器(--it)会移动到下一个元素. • 除了forward_list之外,其他容器都支持反向迭代器.我们可以通过调用rbegin.rcend.crbegin和crend成员函数来获得反向迭代器.这些成员函数返回指向容器尾元素和首元素之前一个位置的迭代器.与普通迭代器一样,反向迭代器也有const和非cons

C++ Primer(六)_OOP_继承

目录 继承 继承 定义相似的类型并对其建模,形成一种层次关系 基类--一般.泛化 派生类--具体.细化 继承的意义 实现代码重用 提高软件易扩展性 Best Practices 为基类定义虚析构函数 理由: 当delete一个动态分配的对象指针时,将执行析构函数,如果指针指向的是继承体系的类型, 可能出现指针的静态类型与动态类型不符的情况, 此时将可能调用了错误版本的析构函数,产生未定义行为 又提重载Overloaded.覆盖(重写)Override与隐藏 重载: 同一作用域,函数名相同而形参列

对C++ Primer的10.3.9单词转换的思考

今天有幸被召回母校给即将毕业的学弟学妹们讲我这两年的工作史,看了下母校没啥特别的变化,就是寝室都安了空调,学妹们都非常漂亮而已..好了不扯蛋了,说下今天的主题吧.这些天我在深度定制语法高亮功能的同时发现了博客园提供的一些有意思的函数,甚至有几个博客园都没用到,我也不知道怎么才能触发那些功能..打开这个js就可以看到很多好用的东西了,虽然写的不怎么样,但是至少有这些功能. ps: 推荐安装一个代码格式化的插件,否则一坨看着蛋疼.比如第一个就是 log,方便调试. http://www.wines-

对C++ Primer的10.3.9单词转换的思考庄专租抓注缀

http://www.ebay.com/cln/r5d_thbr/2015-01-30/166738643014 http://www.ebay.com/cln/1vz_jbnv/2015-01-30/166653044017 http://www.ebay.com/cln/ntx_ntnd/2015-01-30/166918029011 http://www.ebay.com/cln/j19_tvlv/2015-01-30/166764397010 http://www.ebay.com/cl