侯捷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__, __ ...
随机推荐
- Linux驱动|rtc-hym8563移植笔记
本文基于瑞芯微rk3568平台,关于该平台快速入手操作,大家可以参考以下文章: <瑞芯微rk356x板子快速上手> 0.什么是rtc-hym8563? RTC:实时时钟的缩写是(Real_ ...
- SimpleRAG:基于WPF与Semantic Kernel实现的一个简单的RAG应用
SimpleRAG介绍 SimpleRAG是基于WPF与Semantic Kernel实现的一个简单的RAG应用,可用于学习与理解如何使用Semantic Kernel构建RAG应用. GitHub地 ...
- vscode注释快捷键
单行注释 ctrl+/ 多行注释 ctrl+alt+a 文档注释 /** 复制上面一行 ctrl+d 选中段落整体向左或向右缩进 ctrl +[ 或 ctrl + ] 查找 ctrl + f
- 如何保证 Redis 的高并发和高可用?讨论redis的单点,高可用,集群
如何保证 Redis 的高并发和高可用?讨论redis的单点,高可用,集群. 打开GitHub搜索redis,边可以看到,该项目的介绍是这样的: Redis is an in-memory datab ...
- ZEGO 教程 | RTC + AI 视觉的最佳实践(移动端)
摘要:帮助开发者在音视频场景中快速获得 AI 视觉功能 -- 美颜.滤镜.背景抠图等. 文|即构 Native SDK 开发团队 Z世代作为社会新的消费主力,追求个性.热爱新奇事物,青睐与酷炫 ...
- 支付宝携手HarmonyOS SDK打造高效便捷的扫码支付体验
背景 在日常的购物转账.生活缴费等在线支付中,用户在正式拉起支付界面前,均需要至少经历一次"识别"+两次"寻找",即识别归属应用.寻找应用.寻找扫码入口,才能完 ...
- frp_v0.37.1内网穿透,内网服务公网用不求人
前言: 公司内网无法访问,出差又需要用到公司内网进行办公,苦恼了好一阵.这时候想到了内网穿透,这就不得不提到几年前被安利的frp,一看GitHub竟然已经5年了,网上估计大把教程了. 那么什么是frp ...
- 工具 – Prettier、ESLint、Stylelint
前言 以前在 Webpack 学习笔记 有稍微介绍过它们.这篇是单独整理版. 参考 一文彻底读懂ESLint 你的ESLint真的需要Prettier吗? 搞懂eslint和prettier等的关系 ...
- Identity – user login, forgot & reset password, 2fa, external login, logout 实战篇
前言 之前写过一篇 Identity – User Login, Forgot Password, Reset Password, Logout, 当时写的比较简陋, 今天有机会就写多一篇实战版. 建 ...
- SQL注入演示
SQL注入演示 创建一个简易的登录系统/** tb_user 用户表 * 用户登录系统(需在数据库中创建tb_user表 ) */ @Test public void testUserLogin() ...