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继承的,那么只能在成员定义中使用这样的代码。

C++ primer读书笔记10-继承

时间: 2024-08-24 03:33:54

C++ primer读书笔记10-继承的相关文章

C++primer读书笔记11-多态

多态也是C++中的一个重要的方面,多态和动态类型,虚函数本质上是指相同的事情. 1 虚函数 类中的成员函数原型前面加上virtual 表面这个函数是个虚函数.虚函数的目的是为了在继承它的派生类中重新定义这个函数,以便于通过基类的指针或引用在运行时对派生类的函数进行调用. 2 派生类和虚函数 派生类一般情况下要重定义所继承的虚函数,有几个注意事项. <1>虚函数的声明必须和基类中的函数声明原型完全一致,例外的是当基类返回基类型的指针或者引用的时候,派生类可以派生类类型的指针或者引用 <2&

C++中的volatile(Primer读书笔记)

时间:2014.05.24 地点:基地 -------------------------------------------------------------------------- 一.简述 volatile限定符平时很少用到,今天倒是碰到了,所幸探个明白.volatile 英文字面意思是"不稳定的",确切的计算机含义时与机器相关,所以在对包含volatile的程序在移植到新机器或使用不同的编译器时往往还需要对编译器进行一些改变. -----------------------

C++primer读书笔记9转换与类类型

有时候指定了自己类类型来表示某种类型数据如SmallInt,那么为了方便计算就会指定一个转换操作符,将该类类型在某种情况下自动的转换为指定的类型 <1>转换操作符 operator type(); 转换函数必须是类成员函数,不能指定返回类型,并且形参列表必须为空,并且通常不应该改变转换对象,所以操作符通常定义为const成员. #include <iostream> using namespace std; class SmallInt { public: SmallInt(int

正面管教读书笔记 10 你的性格对孩子性格的影响

正面管教读书笔记 10 你的性格对孩子性格的影响 正面管教 作者:简·尼尔森(Jane Nelsen) 第10章 你的性格对孩子性格的影响 书中通过"生活态度取向",将我们分为:安逸型,控制型,取悦型,力争优秀型. 个人看法:这种分类并不专业.不正确,模棱两可比较多. 好的一方面,是从总体看(不要一个个看),还是一种举例的方式,描述正确的方向是什么. 四种生活态度取向 取向 最担心的 避免倾向 安逸 情感和生活的痛苦与压力:他人的期望:受人迫胁 追求安逸:寻求别人的关照:让别人感到舒适

c++ primer读书笔记之c++11(二)

1 新的STL模板类型,std::initializer_list<T> c++11添加了initializer_list模板类型,用于提供参数是同类型情况的可变长度的参数传递机制,头文件是<initializer_list>. 其具体接口可参考cplusplus.com的介绍,地址如下:http://www.cplusplus.com/reference/initializer_list/initializer_list/?kw=initializer_list 与vector不

《Head First Java》读书笔记——7 继承与多态

(一)继承范例 1.范例一: 1 pulic class Doctor{ 2 boolean worksAtHospital; 3 void treatPatient(){ 4 //执行检查 5 } 6 } 7 public class FamilyDoctor extends Doctor{ 8 boolean makesHouseCalls; 9 void giveAdvice(){ 10 //提出诊断 11 } 12 } 13 public class Surgeon extends Do

c++ primer读书笔记之c++11(三)

1 =default构造函数限定符 c++11针对构造函数提供了=default限定符,可以用于显式指定编译器自动生成特定的构造函数.析构或赋值运算函数.参考代码如下: class CtorDftType { public: CtorDftType()=default; CtorDftType(const CtorDftType&)=default; CtorDftType & operator = (const CtorDftType &)=default; ~CtorDftTy

函数(C++ Primer读书笔记)

C++ Primer 第五版课后题 练习6.32 :下面的函数合法吗?如果合法,说明其功能:如果不合法,修改其中的错误并解释原因. #include <iostream> using namespace std; int &get(int *arry, int index) { return arry[index]; } int main() { int ia[10]; for (int i = 0; i != 10; ++i) get(ia, i) = i; return 0; }

C++ Primer 读书笔记

1,命令编译生成的默认输出文件(可执行文件) 命名为:a.out(Unix), a.exe(Windows) 2,cout输出首先会存到缓存中,而printf之类的输出会直接输出到输出流中. 3,可以从键盘上输入End-Of-File:Ctrl+d(Unix), Ctrl+z(Windows). 4,C++中最常见的三种编译错误:1)类型错误,将值赋给不同类型的变量:2)声明错误,使用未声明的变量,或者在同一个域中重复声明一个变量:3)语法错误,写错语法. 5,可以通过command <infi