C++Primer学习笔记(14)

这篇笔记的内容是面向对象程序设计。

面向对象三大基本特性:封装(即数据抽象)、继承、跟多态(静态跟动态)。其中封装在之前的

C++Primer学习笔记(6)_qq_42987967的博客-CSDN博客

做了简单的关于数据抽象与封装入门介绍,而在上一章:

C++Primer学习笔记(13)_qq_42987967的博客-CSDN博客

则介绍的静态绑定:重载,本章则介绍动态绑定:虚函数。除此之外本章还会介绍继承。

这章结束完成,那么对面向对象便会有一个进阶式的飞跃了。

一、oop概述

  关于接口与实现分离可参考:接口与实现分离 - Lckfa - 博客园

1.继承

 这一块没什么好讲的,都是最基础的面向对象知识。

 2.动态绑定

动态绑定是面向对象中最有意思的地方。 

二、定义基类和派生类

接下来接受的部分是也许有点熟悉,但是都是一些模模糊糊的点。看这一块的话可以先不要太钻牛角尖,等整个都看完了再结合例子应该会感觉恍然大悟。

 1.定义基类

为什么基类的析构函数要加virtual呢? 得先记住这个疑问。

1)成员函数与继承

 2)访问控制与继承

 2.定义派生类

 大致说明了下访问说明符

 1)派生类中的虚函数

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

 举例说明:通过理解不同的对象指针的函数调用可以理解这个动态绑定的过程。

class father {
public:
	virtual void f1() { cout << "fatherf1()" << endl; };//
	//virtual void f2() = 0;
	virtual void f3() { cout << "fatherf3()" << endl; };//
};
class son :public father {
public:
	void f1() { cout << "sonf1()" << endl; };//
	//void f2() override{ cout << "sonf2()" << endl; };
	void f3() override { cout << "sonf3()" << endl; };//
	void f4() { cout << "sonf4()" << endl; };
};

void main()
{
	father* f = new son();// = new father();//= new father()不加public,这个赋值不论
	f->f1(); 
	//f->f2();
	f->f3(); //f->f4();
	son* s = new son();
	f = s;
	f->f1();
	//f->f2(); 
	f->f3();
	father* f1 = new father();
	//f1 = f;
	f1->f1();
	//f->f2(); 
	f1->f3();
	father& f2 = *f;
	f2.f1();
	//f->f2(); 
	f2.f3();
}

3)派生类构造函数

 分为基类部分跟派生类自有部分两部分初始化。

4)派生类使用基类成员

 5) 继承与静态成员

 静态成员与其他普通可继承对象成员的不同之处在于他无论怎么继承,都只指向同一块内存空间。

6)派生类的声明

 还是得注意区分声明与定义的区别。

7)被用作基类的类

8)防止继承的发生

 加上final关键字可以使类无法成为基类,在C#当中称其为密封类。

3.类型转换与继承

怎么理解这个强转关系呢?我举个例子:

    double d = 1;
    int *p = &d;//报错。无法强转
	int& r1 = d;//报错。无法强转
	const int& r = d;//假强转,r并没有绑定在d上。详见C++Primer学习笔记(1)四2)

上面两个不同类型的指针和引用的强转是会报错的。但是基类和派生类的指针与引用之间却存在的可行的强转关系,是指针和引用强转中的特例,所以被单独拎出来了。基类与派生类之间的隐式强转的例子见上面的二.2.2)

1)动态类型与静态类型

很绕,参考一下这个吧:C++动态类型与静态类型_赵同学的博客-CSDN博客_c++动态与静态 

比较值得注意的是只有指针和引用才可能会出现动态类型与静态类型不一致的情况。

 2)不存在从基类到派生类的隐式转换

这么说吧。派生类能强转为基类,但是基类无法强转为派生类。

可以这样理解:因为派生类中除了继承基类的成员还有自己独有的成员,其转换为基类时可以丢掉自己私有的成员。而基类要转为派生类,却不能凭空造出那些派生类独有的成员。

 3)对象之间不存在类型转换

 跟上面的静态类型与动态类型里说的一样,只有指针和引用才可能会出现动态类型与静态类型不一致的情况。这句话是重点,后面会反复出现

三、虚函数 

1.虚函数的调用可能再运行时才被解析

 2.派生类中的虚函数

3.final和override说明符

4.虚函数与默认实参

 没太理解这段话的意思。

5.回避虚函数的机制

 即让虚函数不进行动态绑定,而是一直使用基类中的虚函数。

四、抽象基类

1.纯虚函数 

 注意理解通用概念和具体策略这两个词。

 2.含有纯虚函数的类是抽象基类

由纯虚函数引出抽象类的概念,抽象类是一种概念,是在向我们阐述某种规范。 

3.派生类构造函数只初始化他的直接基类

 重构是一个很大的概念,可参考:如何实施代码重构? - 知乎

五、访问控制与继承

 1.受保护的成员

 2.访问关系与继承

 总结一下:

a.无论哪种继承方式,在一级派生类内部,除了private无法访问,其他都可以访问。

b.private方式的一级派生类,会把所有能继承到的成员设置成为自己的私有成员。

class father {
public:
	int k;
};
class son :public father {
};
class son1 :public father {
};
class son2 :private father {
};
void main()
{
    son s; s.k = 1;	
    son1 s1; s1.k = 1;//报错,k为protected
	son2 s2; s2.k = 1;//报错,k为private
	testvirtual();
}

 3.可访问性与派生类向基类的转换之间的关系

这部分我们要从一个类设计者的角度去考虑,不得不说,以当前的功力要设计考虑各个类的访问关系是一件令人头疼的事。

4.友元与继承 

5.改变个别成员的可访问性

采用using可以更改派生类中继承而来的成员的可访问性。不禁感慨这C++真是灵活多变呀 。

 6.默认的继承保护级别

值得重点注意的就是:struct也是类,不要将其特殊对待。然后感觉还是少用struct,这样表达的继承关系不清晰。

六、继承中的类作用域

1.在编译时进行名字查找

这应该就是说名字的解析查找发生在编译时。 

 2.名字冲突与继承

大致就是如果派生类出现与基类同名的函数,派生类成员在调用该函数时会隐藏基类的该同名函数。 

3.通过作用域运算符来使用隐藏的成员

 名字查询的过程值得品味,先从静态类型找,然后再从继承链中找。

4.名字查找优先于类型检查

 5.虚函数与作用域

 

6.覆盖重载的函数

七、构造函数与拷贝控制

1.虚析构函数

 虚析构函数是一个挺重要的知识点了。不好好用可能就内存泄漏了。

额外的细节

这个得当成知识点记起来。

 2.合成拷贝控制与继承

class Base {
public:
	virtual ~Base(){
		cout << "delete Base" << endl;
	}
};
class Derive :public Base {
public:
	~Derive() {
		cout << "delete Derive" << endl;
	}
};

void Learn14OOP::main()
{
	Derive d;
	Derive* d1 = new Derive();
	delete d1;
	Base* d2 = new Derive();
	delete d2;
	return;
}

 ​​​​​​​

 哪怕是虚析构函数,也是会按照继承链来走。只要是调用了派生类的析构函数,那么必然会调用其基类的析构函数的。

1)派生类中删除的拷贝控制与基类的关系

只要能理解派生类是由基类部分和自己的部分这两部分构成的,那么再理解这个就挺好理解了。 

2)移动操作与继承

 推测应该是基类有了移动构造后,那么派生类将继承其并合成自己的移动构造。

3.派生类的拷贝控制成员

 析构函数会逐个调用,先调用派生类的,再调用基类的。

1)定义派生类的拷贝或移动构造函数

 

派生类的拷贝构造函数或移动构造函数想要初始化基类部分的值时需要在派生类的拷贝构造函数或移动构造函数中显示使用基类的拷贝构造或移动构造函数。

 2)派生类赋值运算符

 3)派生类析构函数

 4)在构造函数和析构函数中调用虚函数

 这一块可参考:C++常见面试题知识点_qq_42987967的博客-CSDN博客

 4,继承的构造函数

比较值得注意的是对构造函数使用using。

 1)继承的构造函数的特点

 构造函数的继承挺复杂的,要注意区分普通构造函数的继承与默认、拷贝、移动构造函数的继承区分开来。最重要的是继承而来的构造函数不会影响派生类去合成默认构造函数。

八、容器与继承

略过

九、文本查询查询再探

略过

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值