侯捷C++高级面向对象编程_下_课程笔记
friend(友元):相同Class的各个objects互为friends(友元)
class complex{
public:
complex (double r = 0, double I = 0) : re (r), im (i) { }
//一个对象的成员函数可以调用另一个对象的私有成员变量,全因为他们是同一个类,默认为友元
int func(const complex& param){
return param.re + param.im;
}
private:
double re, im;
};
array new 一定要搭配 array delete

delete的两步删除操作执行的第一步是析构,第二步是free()
若array new 没有array delete的话,析构并不会将数组中所有的变量析构,会造成new数组中变量的内存泄露
Conversion function,类型转换函数
将一种类的对象的类型转换成其他类型的函数
class Fraction
{
public:
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den){}
operator double() const { //类型转换
return (double) (m_numerator / m_denominator);
}
private:
int m_numerator; //分子
int m_denominator; //分母
}
调用举例:
Fraction f (3, 5);
double d = 4 + f; //调用operator将f转为0.6
第二种Non-explicit-one-argument ctor,无explicit且只需一个实参的构造函数
class Fraction
{
public:
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) { }
Fraction operator+(const Fraction& f) {
return Fraction(......);
}
private:
int m_numerator; //分子
int m_denominator; //分母
}
调用举例:
Fraction f (3, 5);
Fraction d2 = f + 4; //会首先调用non-explicit ctor将4转换成Fraction类型
//然后再调用operator+
问题:当出现多条可选方案进行类型转换的时候编译器会报错产生歧义,例如在同时拥有类型转换和类型+的时候,double d = f + 4; //可f转double,也可以4转F再转double
第三种explicit-one-argument ctor(明确的只需一个实参的构造函数)
class Fraction
{
public:
explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) { }
operator double() const{
return (double) (m_numerator / m_denominator);
}
Fraction operator+(const Fraction& f) {
return Fraction(......);
}
private:
int m_numerator; //分子
int m_denominator; //分母
}
调用举例:
Fraction f (3 ,5);
Fraction d2 = f + 4; //Error:编译器未能将double转为Fraction
//加上explicit之后的构造函数只能在构造的时候调用
pointer-like classes(关于智能指针):
template<class T>
class shared_ptr
{
T& operator*() const { return *px; }
T* operator->() const { return px; }
shared_ptr(T* p) : px(p) { }
private:
T* px;
long* pn;
...
}
调用举例:
struct Foo
{
...
void method(void){}
}
shared_ptr<Foo> sp(new Foo);
Foo f(*sp); //指针的解引用实现,基于Operator*实现
sp->method(); //指针指向变量的内部函数的调用,等同于px->method
//特别的有,->运算的特别:箭头作用不会调用一次就停止,会继续作用下去,所以sp->等于px->
pointer-like classes(关于迭代器):
不同于一般的智能指针,迭代器还需要它所特有的功能++\--,且他的操作符重载亦有不同
T& operator*() const
{ return (*node).data; }
T* operator->() const
{ return &(operator*()); }
调用举例:
T& operator*() const
{ return (*node).data; }
T* operator->() const
{ return &(operator*()); } //返回这个对象的地址
list<Foo>::iterator ite;
...
*ite; //获取一个Foo object
ite->method();
//意思是调用Foo::method();
//相当于(*ite).method();
//相当于(&(*ite))->method();
Function-like classes,所谓仿函数:即通过()调用的类
template <class Pair>
struct select1st {
const typename Pair::first_type&
operator() (const Pair& x) const
{ return x.first; }
}
调用举例:
select1st<pair>()(x); //第一个()是生成对象,第二个()是调用operator()成员函数
member template成员模板·:
template<class T1, class T2>
struct pair {
...
T1 first;
T2 second;
pair():first(T1()),second(T2()) {}
pair(const T1& a, const T2& b):
first(a),second(b) {}
template <class U1, class U2>
pair(const pair<U1, U2>&p):
first(p.first), second(p.second) {}
}
调用举例:
例如在pair类中,我们可用通过调用任意的满足模板类型的U1、U2来构造对象,只要他们能满足其自身能转换成T1、T2
具体表现在U1、U2为子类,T1、T2为父类时,可用转换
同样也可以表现在其他可以进行类型转换的场景如:
pair<double,double> p({1,1}); //整数转小数
specialization,模板特化:
泛化的反义词,根据特殊的类型进行特殊的处理
template <class Key>
struct hash {};
template <>
struct hash<char> {
size_t operator() (char x) const { return x; }
};
template <>
struct hash<int> {
size_t operator() (int x) const { return x; }
};
调用举例:
cout << hash<int>() (1000); //第一个()产生一个临时对象,第一个括号调用成员函数
partial specialization,模板偏特化——个数的偏:只将有限个数的参数绑定为特定的类型
template<typename T, typename Alloc=...>
class vector
{
...
};
template<typename Alloc=...>
class vector<bool, Alloc>
{
...
}
——范围的偏:将任意类型缩小到任意类型的指针
template <typename T>
class C
{
...
};
template <typename U>
class C<U*>
{
...
};
template template parameter, 模板模板参数
template <typename T,
template <template T>
class Container
>
class XCls
{
private:
Container<T> c;
...
};
template<typename T>
using Lst = list<T, allocator<T>>;
调用举例:
XCls<string, Lst> mylst;
解释:
在定义模板时定义了模板中的模板,使得程序更加泛化
特殊的有:
当第一个模板会限制第二个模板时泛化会被限制,故其不能称为模板模板参数,且写法不同
template <class T, class Sequence = deque<T>>
class stack {
...
protected:
Sequence c; //底层容器
};
调用举例:
stack<int, list<int>> s;
variadic templates:数量不定的模板参数
void print()
{
}
template <typename T, typename... Type>
void print(const T& firstArg, const Types&...args)
{
cout<<firstArg<<endl;
print(args...); //递归处理
}
调用举例:
print(7.5, “hello”, bitset<160>(377),42); //实际调用四次,当最后print传入为空时调用重载
特殊的有:
当要获取args中参数个数时使用
sizeof…(args);
reference:引用
编译器的假象(好的假象):当r是x的引用时
sizeof(r) == sizeof(x); //实际引用只占4字节
&r == &x;
常见用途:
void func1(Cls obj) { ob.xxx(); }
void func2(Cls& obj) { ob.xxx(); } //被调用端用法也相同
void func3(Cls* obj) { ob->xxx(); }
...
Cls obj;
func1 (obj);
func2 (obj); //接口相同,方便调用
func3 (&obj); //接口不同,会造成困扰
特别的有:
reference通常不用于声明变量,而用于参数类型(parameters type)和返回类型(return type)的描述。
以下被视为“same signature”(所以二者不能同时存在):
double imag (const double& im) { … };
double imag (const double im) { … }; //Ambiguity,但是可以在()后加const加以区分
Inheritance(继承)关系下的构造和析构:
构造由内而外:子类的构造函数先调用父类的default构造函数,然后才执行自己
Derived::Derived(…) : Base() {…}; //构造时编译器自动调用父类进行构造
Derived::~Derived(…) { … ~Base() }; //析构时自动添加父类析构
Composition(复合)关系下的构造和析构:
同样的构造由内而外,析构由外而内
构造时先构造内部包含的类,析构时先析构外部的容器类
对象模型(Object Model) : 关于vptr和vtbl (虚指针和虚表)
对于非虚成员函数其在类中独立创建,对于虚函数,其使用虚表和虚指针来实现,虚表中包含该类的所有虚函数地址,子类虚表包含一切父类的虚函数地址,但如果对虚函数进行了重载则会让虚表中该函数的虚指针指向它重新创建的虚函数。

根据c的语法调用虚函数即(* (p->vptr)[n] ) (p) 或 (* p->vptr[n] ) (p);
其在具体的子类调用时为(*(this->vptr)[n])(this);
对象模型(Object Model) : 关于Dynamic Binding
静态绑定时,函数调用时call直接通过获取静态函数地址来调用
动态绑定时,汇编代码call通过虚指针vptr来获取函数地址以实现函数动态绑定
关于面向对象中的const:
const object不可以调用non-const成员函数
const成员函数不可以调用non-const成员函数
实际举例:
Class template std::basic_string<…>有如下两个成员函数:
charT
operator[] (size_type pos) const
{ ....../* 不必考虑COW*/ }
reference
operator[] (size_type pos)
{ ....../*必须考虑COW*/ } //COW即 copy on write
当多个对象共用一个字符串时,如果某个对象想要改变该字符串,即它需要考虑COW,即使用下面那个成员函数,如果不存在改变的情况则两个都可以使用
但!,C++又规定有:当成员函数的const和non-const版本同时存在时,const object只会(只能)调用const版本,non-const object只会(只能)调用non-const版本
关于operator new和operator delete
new由三步组成:operator new(底层调用malloc获取void指针指向的要求大小空间)、static_cast<type>(对上一步malloc申请的void指针做强制类型转换)、构造函数调用
delete由两步组成:析构函数、operator delete (底层调用free)
new\delete虽然不可以重载,但其中operator new\delete可以重载,既可以全局重载也可以成员函数重载(一般用来写内存池)
例如:对单个对象进行new/delete
class F00 {
public:
void* operator new(size_t);
void operator delete(void*, size_t);
//delete的size_t参数可选填...
}
对数组对象(per-class allocator)进行new/delete
class F00 {
public:
void* operator new[](size_t);
void operator delete[](void*, size_t);
//...
}
调用时:
Foo* p = new Foo[N];
等于
try{
void* men = operator new(sizeof(Foo) * N);
p = static_cast<Foo*>(men);
p->Foo::Foo(); //N次
}
dwelete []p;
等于
p->~Foo(); //N次
operator delete(p);
如果没有重载operator new\delete或Foo* pf = ::new Foo; / ::delete pf;
就会调用就调用globals:void* ::operator new(size_t); / void ::operator delete(void*)
我们可以重载多个版本的class member operator new(),前提是每个版本都要有其独一无二的参数列表,其中第一个参数必须是size_t,其余参数根据需求添加。
同样我们可以重载多个版本的operator delete(),但他们绝不会被delete调用。只有当new所调用的ctor使用之前定义的对应版本的operator new()时抛出异常,才会调用这些重载版本的operator delete(),主要用来归还未能完全创建成功的对象所占用的内存。
即使operator delete(…)未能一一对应于operator new(…),也不会出现报错,系统默认你放弃处理ctor发出的异常
侯捷C++高级面向对象编程_下_课程笔记的更多相关文章
- 侯捷《C++面向对象开发》——动手实现自己的复数类
		
前言 最近在看侯捷的一套课程<C++面向对象开发>,刚看完第一节introduction之后就被疯狂圈粉.感觉侯捷所提及所重视的部分也正是我一知半解的知识盲区,我之前也写过一些C++面向对 ...
 - Python面向对象编程(下)
		
本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...
 - Python全栈day24(面向对象编程作业作业_定义学校老师课程班级学生类)
		
面向对象作业 作业_定义学校老师课程班级学生类.py #面向对象编程作业,定义学校老师课程班级学生类 #定义几个类,尽可能定义多的数据属性及函数属性 class School: def __init_ ...
 - .net 4.0 面向对象编程漫谈基础篇读书笔记
		
话说笔者接触.net 已有些年头,做过的项目也有不少,有几百万的,也有几十万的,有C/S的,也有B/S的.感觉几年下来,用过的框架不少,但是.net的精髓一直没有掌握.就像学武之人懂得各种招式,但内功 ...
 - 『TensotFlow』RNN中文文本_下_暨研究生开学感想
		
承前 接上节代码『TensotFlow』RNN中文文本_上, import numpy as np import tensorflow as tf from collections import Co ...
 - Css深入理解之浮动_慕课网课程笔记
		
前言 这篇是在慕课网上跟着张鑫旭重走CSS之路的第三篇学习笔记了,主要是学习float属性,闲话少说,下面进入正文. float的历史 要想了解一个东西,我们还是需要从本质去了解它,那么我们就需要问一 ...
 - Python 并发编程(下)
		
Python 并发编程(下) 课程目标:掌握多进程开发的相关知识点并初步认识协程. 今日概要: 多进程开发 进程之间数据共享 进程锁 进程池 协程 1. 多进程开发 进程是计算机中资源分配的最小单元: ...
 - 进击的Python【第六章】:Python的高级应用(三)面向对象编程
		
Python的高级应用(三)面向对象编程 本章学习要点: 面向对象编程介绍 面向对象与面向过程编程的区别 为什么要用面向对象编程思想 面向对象的相关概念 一.面向对象编程介绍 面向对象程序设计(英语: ...
 - 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值
		
第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...
 - python笔记 面向对象编程从入门到高级
		
目录: 一.概念 二.方法 2.1组合 2.2继承 2.3多态 2.4封装 2.5归一化设计 三.面向对象高级 3.1 反射(自省) 3.2 内置方法__getatter__, __ ...
 
随机推荐
- zabbix 4.0修改页面LOGO
			
基本页面展示 一.Logo icon-sprite.svg是一个集合的图片,logo和一级菜单栏里面的图标是在这上面平移得到的 第一种方法 1. zabbix安装好以后的默认LOGO如下: 2. ...
 - C++开发分类
			
1.基础架构 2.音视频领域 3.安全方向 4.Linux虚拟化 5.Qt客户端.上位机 6.游戏领域 7.嵌入式 8.量化券商 暂时对基础架构.音视频和安全方向较为感兴趣.
 - 初三年后集训测试 T2--牛吃草
			
初三年后集训测试 $T 2 $ 牛吃草 一言难尽 $$HZOI$$ $ Description $ 由于现代化进程的加快,农场的养殖业也趋向机械化. \(QZS\) 决定购置若干台自动喂草机来减少自己 ...
 - 在 Python 中通过读取 .env 文件获得环境变量
			
在编写 Python 脚本时,我们会使用一些私密数据,如调用 API 时使用的 token.为了避免隐私泄露,这些私密数据一般不直接写入脚本文件中.而是写入一个文件,并通过读取文件的方式获取私密数据内 ...
 - VMware Workstation虚拟机 + 许可证密钥
			
VMware Workstation虚拟机 + 许可证密钥 VMware Workstation是什么? VMware简介 VMware 安装 VMware系统要求 VMware 版本下载地址 许可证 ...
 - Openharmony 跑 CV 应用
			
最近有个项目,老同学让帮忙验证一个在ARM 板上跑 OpenHarmony,然后再集成一个CV算法上去,写这个文章主要是整理一下思路.如果有思路不对的地方,也烦请指出. 1. 个人做纯软件比较多,所以 ...
 - 编译和分发 Chez Scheme 应用程序
			
参考 Building and Distributing Applications. 假设源码由两个文件组成,A.ss 和 B.ss,其中 A.ss 依赖 B.ss.下面我们将其编译为可供分发的二进制 ...
 - 机器学习--决策树算法(CART)
			
CART分类树算法 特征选择  我们知道,在ID3算法中我们使用了信息增益来选择特征,信息增益大的优先选择.在C4.5算法中,采用了信息增益比来选择特征,以减少信息增益容易选择特征值多的特征的问题. ...
 - JavaScript – Symbol
			
前言 Symbol 是 es6 的特性. 如果只是写业务逻辑代码, 其实是不太会用到的. 如果是做架构, 封装, UI 组件才有需要. 但学它的概念是好的. es6 有需要内置的 Symbol 链接者 ...
 - docker 安装启动jenkins 以及问题剖析
			
docker 安装启动jenkins 以及问题剖析 首先,你环境必须要有docker,我这里是自己本地虚拟机Vmware,我的虚拟机时linux centos7的 .如果你不知怎么安装虚拟机和命令 ...