侯捷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__, __ ...
随机推荐
- 批量删除git tag
批量删除远程tag $ git ls-remote -t --refs -q | awk '{print ":"$2}' | xargs git push origin To ss ...
- WinUI 3学习笔记(1)—— First Desktop App
随着Visual Studio 2019 16.10版本的正式发布,创建WinUI 3的APP对我们来说,已不存在任何的难度.本篇我们就试着来一探究竟,看看WinUI 3 APP到底是个啥玩意,能不能 ...
- manim边学边做--直线类
直线是最常用的二维结构,也是构造其他二维图形的基础.manim中针对线性结构提供了很多模块,本篇主要介绍常用的几个直线类的模块. Line:通用直线 DashedLine:各种类型的虚线 Tangen ...
- Kubernetes-6:Pod生命周期介绍(init Container)
Pod生命周期 生命周期 1.API server调用kubelet下达Pod创建指令 2.容器环境初始化 3.进入Pod生命周期内(Pod开始创建) 4.Pod只要创建,就会自动生成一个pause容 ...
- express项目的创建
前言 前端开发者若要进行后端开发,大多都会选择node.js,在node生态下是有大量框架的,其中最受新手喜爱的便是老牌的express.js,接下来我们就从零创建一个express项目. 安装nod ...
- Java是值传递还是引用传递,又是怎么体现的
关于Java是值传递还是引用传递,可以从代码层面来实现一下拿到结果 执行下面的代码: public static void main(String[] args) { int num = 10; St ...
- FCA-FineBI最新版考试答案,全全全!!!
FCA-FineBI最新版考试答案,全全全!!!-CSDN博客同博客 Part.1:判断 第1题 判断题 「TODATE」函数或者「DATE」函数,可以将文本字段或数值字段转变成时间类型的字段.(得 ...
- C# – class, filed, property, const, readonly, get, set, init, required 使用基础
前言 心血来潮,这篇讲点基础的东西. Field 比起 Property,Field 很不起眼,你若问 JavaScript,它甚至都没有 Field. 但在 C#,class 里头真正装 value ...
- ASP.NET Core – Program.cs and Startup.cs 小笔记
前言 ASP.NET Core 6.0 以后, 默认模板去掉了 Program.cs 的 namespace, class 和 Startup.cs, 一开始看会有点懵. 这篇大概记入一下, prog ...
- 揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证
揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证 介绍 JWT(JSON Web Tokens)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在网络上安全地传输信息. ...