[toc]
C++面向对象高级开发(全集)
https://www.bilibili.com/video/BV1K4411974P?p=1
C++编程简介
C++ 演化
书籍推荐:
effective C++
stl源码剖析
C vs C++ 关于数据和函数
c通过type(built-in,struct)创建出变量
C++通过class,struct(包含成员和函数)创建出对象
类的经典分类:带指针(复数)和不带指针(string)
object based vs. object oriented
Object based:面对单一class的设计
Object Oriented:面对多重classes的设计,类之间的关系。
C++程序的代码基本形式
.h (“ ”)+ .cpp + .h(标准库,<>)
c和c++的输出
cout和printf
头文件中的guard声明
第一次引用才定义,定义过则不过重复引用
#ifndef __header_name__
#define __header_name__
#endif
头文件的布局
#ifndef __header_name__
#define __header_name__
1.前置声明
class ostream;
class complex;
2.类-声明
class complex
{
}
3.类-定义
complex::function..
#endif
class的声明(declaration)
class complex //class head
{
//class body
}
模板(template)简介
template<typename T>
构造函数
inlinx(内联)函数
函数在class body内定义完成
运行较快。函数体简单
(最终由编译器决定是否为内联)
(现在编译器自动进行内联优化)
access level 访问级别
public
private
protect
构造函数 constructor
名称同类名,不需要返回值类型。
构造函数的特别语法↑
initialization list (大气!!!)
简单的差别:赋值过程不一样,能提高效率
不带指针的类多半不用写析构函数
构造函数可以有很多个--overloading(重载)
编译器编译后形成两个名称的函数
参数传递与返回值
singleton 单例 类
构造函数放在private里
const member functions 常量成员函数
在函数后加const -->拿数据而不改变数据内容
大家风范!
参数传递:pass by value(形参) vs. pass by reference(to const)
引用& 在底层相当于传递指针一样快。
(侯捷老师:良好习惯:最好传引用)
如果不想修改原引用值
使用const name&
参数一字节/两字节:可以形参
返回值传递:return by value vs. return by reference(to const)
返回值 如果可以--> 使用 引用传递
friend(友元)
友元函数可以自由取得friend 的 private成员
相同class的各个objects 互为friends(友元)
class body外的各种定义(definitions)
什么情况下可以pass by reference
什么情况下可以return by reference
- 一个函数运算结果的存放位置,如果是新开辟的local 变量,则不可以使用引用。
- 运算结果存在位置在函数结束后不会释放,则可以使用引用传递
操作符重载与临时对象
操作符重载-1 operator overload 成员函数 this
重载函数可以是成员函数或非成员函数
<< 只能写成非成员函数
传递者无需知道接收者是以reference形式接收
临时对象 typename();
--> return by value
类名typename(),-->创建一个临时对象-->类似于int() 强制类型转换?
注意点
- 考虑该不该加const
- 传递 是否用引用
- 返回是否用引用
- 数据放在private
- 函数 位置
复习complex类的实现过程
考虑函数是否加const
三大函数:拷贝构造、拷贝复制、析构函数
Class with pointer member
举例:string class
指针类构造函数设计
带指针要自己写赋值拷贝
框中一套函数
第二行 拷贝构造
第三行 拷贝赋值(带指针得类一定要写)
第四行 析构函数(释放动态内存分配到的内存)
//一出手就要不同凡响
拷贝构造
默认的赋值会将两个指针指向同一地址
而不是开辟一块新的内存地址存放字符串。
浅拷贝:指针拷贝,将第二个指针指向同一地址->可能会造成内存泄漏
深拷贝:开辟一块新的内存空间,存放数据,并将新的指针指向新的地址。
拷贝赋值函数
↑经典写法->大家风范
先释放掉自己,然后new,然后拷贝。
一定要在op= 中检查是否为自我赋值
堆 heap 栈stack 与内存管理
介绍了heap、stack的概念 作用域
static 在作用域结束之后仍然存在,直到程序结束
heap object 结束时需要delete 掉
new:先分配memory 再调用构造函数
delete:先调用析构函数,再释放memory
动态分配所得的memory block in VC
动态分配的array
array new [] 搭配 array delete []
复习string类的实现过程
模板类、函数模板及其他
static
static静态数据只有一份(记得 在类外 定义/初始化)
type classname::name = 8.0;
静态函数 只能 存取 处理 静态数据
调用static函数的方式:
1.通过对象调用
2.通过class name 调用
单例类 只有唯一一个
单例模式/singleton
cout
类模板 class template
template<typename T>
function template 函数模板
template<class T>
编译器会自动推导出参数是哪类
特化/偏特化 等等
namespace
using directive;//不用写全名
using declaration;//打开某一行或几行
更多细节与深入
组合与继承
类间的关系
- Inheritance(继承
- Composition(复合
- Delegation(委托
composition(复合),表示has-a
类内包含另一个类
//适配器模式
内存观察
composition关系下的构造和析构
构造函数由内而外
析构函数由外而内
Delegation(委托)。compostion by reference
rep是指针指向(与复合相比,寿命不一致,不是同步创建)
Inheritance(继承),表示is-a
父类数据可以完全继承
虚函数搭配最有价值
继承下的构造和析构
- 构造由内而外:先父类,再子类
- 析构由外而内:先子类再父类
虚函数与多态
继承和虚函数
- non-virtual函数:不希望子类重新定义的函数
- virtual函数:希望子类重新定义,并且已有默认定义(可以是空虚函数)
- pure virtual函数:希望子类一定要重新定义,且没有默认定义
委托相关设计
23个经典设计模式
- 类设计的三把大刀。(复合 委托 继承)
导读
conversion function 转换函数
重载了double转换类型
non-explicit-one-argument ctor
explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了
这种情况会出现语法冲突。
explicit-one-argument ctor
- explict 只用在构造函数前
pointer-like classes,关于智能指针
智能指针->share_ptr->像指针的类
智能指针在语法上有三个很关键的地方,第一个是保存的外部指针,对应于上图的T* px,这个指针将代替传入指针进行相关传入指针的操作;第二个是重载“*”运算符,解引用,返回一个指针所指向的对象;第三个是重载“->”运算符,返回一个指针,对应于上图就是px。
关于迭代器
迭代器也是一种智能指针,这里也存在上面提到的智能指针的三个要素
注意操作符重载
创建一个list迭代器对象,list::iterator ite;这里的list用于保存Foo对象,也就是list模板定义里的class T,operator()返回的是一个(node).data对象,node是__link_type类型,然而__link_type又是__list_node<T>类型,这里的T是Foo,所以node是__list_node<Foo>类型,所以(node).data得到的是Foo类型的一个对象,而&(operator())最终得到的是该Foo对象的一个地址,即返回Foo* 类型的一个指针。
function-like classes 仿函数
让类像 一个函数??
“()”函数调用操作符(这我还真的第一次知道这么叫)
重载符号"()",就成为了一种仿函数
都会继承一些奇怪的父类->在c++标准库课程中讲解
namespace 经验谈
隔离一些可能命名重复的函数,避免冲突
class template,类模板
同之前
function template 函数模板
member template 成员模板
黄色代码段本身是一个模板中的成员,它自身又是一个模板。
成员模板在泛型编程里用得较多,为了有更好的可扩展性,以上图为例,T1往往是U1的基类,T2往往是U2的基类
通过这种方法,只要传入的U1和U2的父类或者祖类是T1和T2,那么通过这样的方式可以实现继承和多态的巧妙利用,但反之就不行了。这样的方式在STL中用得很多
specialization 模板特化
泛化->模板
特化->模板中指定特定的数据类型
partial specialization 模板偏特化
1.个数上的偏
2.范围上的偏
使用指针指向任意类型
template template parameter 模板的模板参数
容器需要好几个模板参数
↑ list<int> 绑定好了类型,不算模板参数
关于c++标准库
- 容器
- 算法
c++11在另一门课
三个主题
variadic templates(c++11) 数量不定的模板参数
允许写任意个数的模板参数 “..."已经作为语法的一部分
多个模板参数进行递归调用。
auto
ranged-base for
for循环的新语法
reference
reference一定要有初值。
初值设置之后不可以改变代表对象。
r = x2 一行相当于给r赋值 使r为5 导致x也为5
const 是函数签名的一部分
复合&继承关系下的构造与析构
同之前
关于vptr和vtbl (虚指针、虚函数表
vptr指向vtbl 然后找到虚函数地址,找到对应函数
定义了三个类,A、B和C,B继承于A,C继承于B,A中有两个虚函数,B中有一个,C中也有一个。编译器将A的对象a在内存中分配如上图所示,只有两个成员变量m_data1和m_data2,与此同时,由于A类有虚函数,编译器将给a对象分配一个空间用于保存虚函数表,这张表维护着该类的虚函数地址(动态绑定),由于A类有两个虚函数,于是a的虚函数表中有两个空间(黄蓝空间)分别指向A::vfunc1()和A::vfunc2();同样的,b是B类的一个对象,由于B类重写了A类的vfunc1()函数,所以B的虚函数表(青色部分)将指向B::vfunc1(),同时B继承了A类的vfunc2(),所以B的虚函数表(蓝色部分)将指向父类A的A::vfunc2()函数;同样的,c是C类的一个对象,由于C类重写了父类的vfunc1()函数,所以C的虚函数表(黄色部分)将指向C::vfunc1(),同时C继承了超类A的vfunc2(),所以B的虚函数表(蓝色部分)将指向A::vfunc2()函数。同时上图也用C语言代码说明了编译器底层是如何调用这些函数的,这便是面向对象继承多态的本质。
this指针
通过一个对象来调用,对象的地址就是this
关于动态绑定 dynamic binding
STL源码分析|c++标准库体系结构与内核分析
认识headers、版本、重要资源
使用一个东西,却不明白它的道理,不高明!
目标:使用c++标准库-》认识c++标准库 ->良好使用c++标准库->扩充c++标准库