C++程序设计兼谈对象模型(侯捷)笔记

news2024/11/19 21:27:23

C++程序设计兼谈对象模型(侯捷)

这是C++面向对象程序设计的续集笔记,仅供个人学习使用。如有侵权,请联系删除。

主要内容:涉及到模板中的类模板、函数模板、成员模板以及模板模板参数,后面包含对象模型中虚函数调用(动态绑定)的具体原理。

参考链接:

Youtube: C++面向对象高级开发(下)

Github:源码和PPT

文章目录

  • C++程序设计兼谈对象模型(侯捷)
    • 2 conversion function 转换函数
    • 3 non-explicit-one-argument constructor
    • 4 pointer-like classes
    • 5 function-like classes
    • 6 namespace经验谈
    • 7 class template 类模板
    • 8 function template 函数模板
    • 9 member template 成员模板
    • 10 specialization 模板特化
    • 11 模板偏特化
    • 12 模板模板参数
    • 13 关于C++标准库
    • 14 三个主题 variadic templates等
    • 15 reference
    • 16 复合&继承关系下的构造和析构
    • 17 (对象模型)关于vptr和vtbl,虚指针和虚表
    • 18 (对象模型)关于this
    • 19 (对象模型)关于Dynamic Binding
    • 后记

2 conversion function 转换函数

conversion function 转换函数

把这种东西转变为别的类型:把Fraction转变为double

class Fraction
{
public:
    Fraction(int num, int den=1)
        : m_numerator(num), m_denominator(den) {}
    operator double() const{ // 转换函数,没有return type返回类型, 没有参数
        return (double) (m_numerator / m_denominator);
    }
    
private:
    int m_numerator; // 分子
    int m_denominator; // 分母
}

// 使用
Fraction f(3, 5);
double d = 4 + f;  // 调用operator double() 将f转换为0.6

3 non-explicit-one-argument constructor

one argument: 只要1个实参就够了, 注意和parameter的区别,下面的Fraction构造函数有两个parameter,但是只有一个argument。

non explicit one argument constructor的作用:可以把别的东西转变为这种类型

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);
double d2 = f + 4;  // 调用non-explicit constructor 将4转为 Fraction(4, 1), 然后调用operator+

如果 double转换和重载+操作符并存,编译器就会产生歧义,会报错

class Fraction
{
public:
    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】ambiguous

explicit-one-argument constructor

explicit关键字的作用是防止类构造函数的隐式自动转换.

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】conversion from "double" to "Fraction" requested.

转换函数在标准库中的例子

template<class Alloc>
class vector<bool, Alloc>
{
public:
    typedef __bit_reference reference;
protected:
    reference operator[](size_type n) {
        return *(begin() + difference_type(n));
    }
 ...
}

struct __bit_reference {
    unsigned int* p;
    unsigned int mask;
    ...
public:
    operator bool() const {return !(!(*p & mask));}
...
}

如下图所示:一个vector里面保存的都是bool值,然后返回的是reference类型,这里就要有bool的转换函数。另外下图中还涉及到一种设计模式:proxy,具体关于proxy的知识不展开。

在这里插入图片描述

4 pointer-like classes

pointer like classes 关于智能指针

为什么要把一个类设计出来像一个指针呢?比指针做的事情更多一点

template<class T>
class shared_ptr
{
public:
    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); // 使用*这个操作符
sp->method(); // 使用->这个操作符

在这里插入图片描述

pointer like classes,关于迭代器

迭代器也像指针,指向一个元素


template<class T>
struct __list_node {
    void* prev;
    void* next;
    T data;
};

template<class T, class Ref, class Ptr>
struct __list_iterator {
    typedef __list_iterator<T, Ref, Ptr> self;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef __list_node<T>* link_type;
    link_type node;
    bool operator==(const self& x) const {return node == x.node;}
    bool operator!=(const self& x) const {return node != x.node;}
    reference operator*() const {return (*node).data;}
    pointer operator->() const {return &(operator*());}
    self& operator++() { node = (link_type)((*node).next); return *this;}
    self operator++(int) { self tmp = *this; ++*this; return tmp;}
    self& operator--() { node = (link_type)((*node).prev); return *this;}
    self operator--(int) { self tmp = *this; --*this; return tmp;}
}
T&
operator*() const {return (*node).data;} // 对迭代器解参考,就是拿链表节点中的data

T* 
operator->() const {return &(operator*());} // 获得上面operator*操作的地址

在这里插入图片描述

5 function-like classes

function like classes,所谓仿函数

重载()符号的用意,就是让这个类创建出来的对象是函数对象。

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 T>
struct identity {
  const T&
  operator()(const T& x) const {return x;}
};

template <class Pair>
struct select1st {
    const typename Pair::first_type&
    operator()(const Pair& x) const
    { return x.first;}
};

template <class Pair>
struct select2nd {
    const typename Pair::second_type&
    operator()(const Pair& x) const
    { return x.second;}
};

在这里插入图片描述

标准库中的仿函数的奇特模样:继承自unary_function

template<class T>
struct identity: public unary_function<T, T> {
  const T&
  operator()(const T& x) const {return x;}
};

template <class Pair>
struct select1st: public unary_function<Pair, typename Pair::first_type> {
    const typename Pair::first_type&
    operator()(const Pair& x) const
    { return x.first;}
};

template <class Pair>
struct select2nd: public unary_function<Pair, typename Pair::second_type>  {
    const typename Pair::second_type&
    operator()(const Pair& x) const
    { return x.second;}
};

还有继承自binary_function的仿函数

标准库中,仿函数所使用的奇特的base classes

在这里插入图片描述

6 namespace经验谈

命名空间起到隔离的作用,不同的命名空间里面可以有相同的函数名、变量名等,但是它们属于不同的范围。

在这里插入图片描述

7 class template 类模板

class template,类模板

在设计一个类的时候,允许某个变量或者参数的类型由使用者任意指定,那么就可以把这个类称为模板类,或者叫类模板。

在这里插入图片描述

8 function template 函数模板

function template,函数模板

在使用时,不用指明参数的type,编译器会进行实参推导

在这里插入图片描述

9 member template 成员模板

member template 成员模板

在标准库中的构造函数中会出现大量的member template,为的是让构造函数更有弹性一些,比如用派生类来初始化基类。

template<class T1, class T2>
struct pair{
    typedef T1 first_type;
    typedef T2 second_type;
    
    T1 first;
    T2 second;
    pair(): first(T1()), second(T2()) {}
    pair(const T1& a, const T2& b): first(a), second(b) {}
 #ifdef __STL_MEMBER_TEMPLATES	
    template<class U1, class U2>  // 成员模板
    pair(const pair<U1, U2>& p): first(p.first), second(p.second) {}
 #endif
};

下面以鲫鱼继承鱼类,麻雀继承鸟类来展示成员函数。

在这里插入图片描述

把一个鲫鱼和麻雀构成的pair,放进一个鱼类和鸟类构成的pair,这个是可以的。

class Base1 {};
class Derived1: public Base1 {};

class Base2 {};
class Derived2: public Base2 {};


pair<Derived1, Derived2> p;
pair<Base1, Base2> p2(p);

pair<Base1, Base2> p2(pair<Derived1, Derived2>()); //把一个派生类1和派生类2构成的pair,放进一个基类1和基类2构成的pair,反过来不可以
template<typename _Tp>
class shared_ptr: public __shared_ptr<_Tp>
{
    template<typename _Tp1>
    explicit shared_ptr(_Tp1* __p)
    	: __shared_ptr<_Tp>(__P) {}
};

Base* ptr = new Derived1;  // up-cast 向上转型是可以的

shared_ptr<Base1> sptr(new Derived1);

智能指针模拟向上转型:可以用派生类来初始化基类

在这里插入图片描述

10 specialization 模板特化

specialization 模板特化:限定模板实现的具体类型,比如下面指定hash的类型为char,int和long。

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;}
};

template<>
struct hash<long> {
    size_t operator()(long x) const {return x;}
};

使用过程如下:

cout << hash<long>()(1000);

在这里插入图片描述

11 模板偏特化

模板偏特化——个数的偏

模板中可以指定某些参数为特定类型。

在这里插入图片描述

模板偏特化——范围的偏

从指向任意类型T,变成指向任意类型的指针*T,范围变小了。

在这里插入图片描述

12 模板模板参数

template template parameter,模板模板参数

可以让模板参数它本身是个类模板。下图中第二个参数为模板模板参数Container,它接收第一个模板参数来实例化自己,比如接收下面T类型。

template<typename T, template<typename T> class Conatainer>
class XCls
{
private:
    Container<T> c;
public:
    ...
}

在这里插入图片描述

13 关于C++标准库

Containers、Iterators、Algorithms、Functors的等使用,有另外一门课进行剖析。

14 三个主题 variadic templates等

variadic templates 可变参数模板

使用3个点…来表示

template<typename T, typename... Types>

void print()
{
    
}
void print(const T& firstArg, const Types&... args)
{
	cout << firstArg << endl;
    print(args...);
}

在这里插入图片描述

auto 用法:自动推导变量类型

之前的用法

list<string> c;
list<string>::iterator ite;
ite = find(c.begin(), c.end(), target);

C++11的用法

list<string> c;
auto ite = find(c.begin(), c.end(), target);

ranged-base for

能传引用就传引用,速度快。

vector<double> vec;

for(auto elem: vec) {
    cout << elem << endl;
}

for (auto& elem: vec) { // 改变原来的数据,要传引用&
    elem *= 3;
}

在这里插入图片描述

15 reference

int x = 0;

pointer to interger: int *p = &x;

reference to interger: int& r = x; r不是指针,r代表x, x的地址在哪,r的地址就在哪。 但是底层实现的时候使用指针实现的。

如果 int x2 = 5; r = x2;由于r已经代表了x,它不能重新代表其他的东西,经过r = x2;之后,r和x都变成了5.

在这里插入图片描述

reference引用的常见用途

在这里插入图片描述

16 复合&继承关系下的构造和析构

单独的继承关系、符合关系的继承与析构请参加我的笔记C++面向对象高级编程(侯捷)笔记2中关于11 组合与继承的部分。

这里记录继承+组合关系下的构造和析构

构造由内而外

Derived的构造函数首先调用Base的默认构造函数,然后调用Component的默认构造函数,然后执行自己。

析构由外而内

Derived的析构函数首先执行自己,然后调用Component的析构函数,然后调用Base的析构函数。

在这里插入图片描述

17 (对象模型)关于vptr和vtbl,虚指针和虚表

下面开始谈Object Model的内容。

虚指针vptr和虚表vtbl

virtual function虚函数有两个步骤来支持:

  1. 每一个class产生出一堆指向virtual functions的指针,放在virtual table(vtbl)中。
  2. 每一个class object被添加了一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设置和重置都由每个class的构造函数、析构函数和拷贝赋值运算符自动完成。

参考:深度探索C++对象模型

继承中,子类的对象里面有父类的成分

调用虚函数的过程,通过vptr得到vtbl,然后查找表中的第n个函数

(*(p->vptr[n]))(p);(*p->vptr[n])(p);

在这里插入图片描述

18 (对象模型)关于this

对象模型object model:关于this

动态绑定的三个条件:

  1. 通过指针调用
  2. 指针有向上转型的动作
  3. 调用虚函数

在这里插入图片描述

编译器对动态绑定动作:

this->Serialize();

编译成下面这种形式:虚指针指向虚表,调用特定的虚函数

(*(this->vptr)[n])(this);

19 (对象模型)关于Dynamic Binding

对象模型:关于Dynamic Binding

静态绑定:直接调用汇编的call指令,跳转到函数地址。

B b;
A a = (A)b;
a.vfunc1(); // 静态绑定,直接call vfunc的地址

在这里插入图片描述

动态绑定:虚指针查虚表得到虚函数的地址,进行调用。

A* pa = new B;  // 向上转型
pa->vfunc1();

在这里插入图片描述

后记

从2024年1月1日开始,截至2024年1月4日,共花费4天,学习完C++面向对象高级编程(上)和C++面向对象高级编程(下),其中后者的标题为C++程序设计兼谈对象模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1358575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【合阳新起点公益】“关爱留守儿童 守护牙齿健康”牙膏发放活动

为了关爱儿童的口腔健康&#xff0c;帮助他们改善生活状况&#xff0c;养成良好的刷牙习惯。合阳县未成年人保护中心、合阳县新起点公益服务中心组织链接到汕头市惠泽人志愿服务中心&#xff0c;为孩子们申请到一批儿童爱心牙膏套盒&#xff0c;分别于2023年12月22日、12月30日…

基于seatunnel实现mysql同步clickhouse验证

场景&#xff1a; 需求想要实现mysql同步到clickhouse&#xff0c;seatunnel部署见前面文档linux环境seatunnel安装运行-CSDN博客。 官方说明文档 Clickhouse | Apache SeaTunnel mysql同步配置 server-id1 log_bin/var/lib/mysql/bin.log binlog_formatROW #binlog-do-db 具…

必看!干得不能再干了,Abaqus各版本安装指南

Abaqus下载链接 https://pan.baidu.com/s/1PHWhlM4cPINUqy2XYRHrhQ?pwd0531 ​1.鼠标右击【Abaqus2023(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Abaqus.2023(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【jdk-17…

2024年MySQL学习指南(四),探索MySQL数据库,掌握未来数据管理趋势

文章目录 前言9. 约束的概念10. 约束的分类11. 非空约束12. 唯一约束13. 主键约束14. 默认约束15. 外键约束16. 约束的案例练习 前言 接上篇&#xff1a; 2024年MySQL学习指南&#xff08;一&#xff09; 2024年MySQL学习指南&#xff08;二&#xff09; 2024年MySQL学习指…

影响机器人行业爆炸式发展的原因有哪些(OpenPilot)

机器人是现在和未来最有发展前景的行业&#xff0c;这一点是毋庸置疑的。 先看一段21年的视频&#xff1a; 外置式L2级别自动驾驶辅助套件 Openpilot 深度体验分析 火热 作为热门专业的从业人员&#xff0c;能够感受到行业的关注热度和实际热度之间的差异。 原因 有很多&am…

操作系统(期末复习知识点)

第1章 操作系统引论 操作系统的目标和作用 操作系统的目标 在计算机系统上配置OS&#xff0c;其主要目的是实现&#xff1a;方便性、有效性、可扩充性、开放性。 操作系统的作用 可以从人机交互、资源管理及资源抽象等不同方面来分析OS在计算机系统中所起到的作用。 OS 作为…

怎么测试电源负载瞬态响应?具体步骤是什么?

负载瞬态响应(Load Transient Response)指的是电路或系统在负载发生瞬态变化时,输出电压或电流出现的瞬时变化的情况。 电源负载瞬态响应测试是评估电源在负载变化时性能表现的一种重要方法。在实际应用中&#xff0c;电源负载的变化是不可避免的&#xff0c;因此准确评估电源的…

面试必问究极重点之HashMap的底层原理

1.底层数据结构 JDK版本不同的数据结构 1.7 数组 链表 1.8 数组 &#xff08;链表 | 红黑树&#xff09; 2.添加数据put 在添加一个值的时候&#xff0c;首先会计算他的hash码&#xff0c;然后进行二次hash&#xff0c;在对当前长度取模得到在底层数组中的索引位置当取模完…

用Spring Boot 3.2虚拟线程搭建静态文件服务器有多快?

Spring Boot 3.2 于 2023 年 11 月大张旗鼓地发布&#xff0c;标志着 Java 开发领域的一个关键时刻。这一突破性的版本引入了一系列革命性的功能&#xff0c;包括&#xff1a; 虚拟线程&#xff1a;利用 Project Loom 的虚拟线程释放可扩展性&#xff0c;从而减少资源消耗并增…

【HBase】——优化

1 RowKey设计 重要&#xff1a;一条数据的唯一标识就是 rowkey&#xff0c;那么这条数据存储于哪个分区&#xff0c;取决于 rowkey 处于 哪个一个预分区的区间内&#xff0c;设计 rowkey的主要目的 &#xff0c;就是让数据均匀的分布于所有的 region 中&#xff0c;在一定程度…

海昌海洋公园用泛微千里聆RPA智能采集、识别、分析网络数据,助力优化运营服务

海昌海洋公园控股有限公司&#xff08;以下简称“海昌海洋公园”&#xff09;是中国知名的主题公园和配套商用物业开发及运营商。经过近二十年发展&#xff0c;凭借行业优秀的极地海洋动物保育技术&#xff0c;公司将其业务模式逐步推广到核心城市&#xff0c;展开了海昌海洋公…

2024中国管业十大品牌——皮尔特管业

2024中国管业十大品牌——皮尔特管业 2024年度中国管业十大品牌评选活动圆满举办。来自江苏的皮尔特管道&#xff0c;再次成功入围2024中国管业十大品牌。皮尔特管业凭借多年积累的市场口碑&#xff0c;再次入围也是实至名归。 苏州皮尔特管业科技有限公司创建于2001年&#x…

矩阵翻转180度是什么意思,请举例

问题描述&#xff1a;矩阵翻转180度是什么意思&#xff0c;请举例 问题解答&#xff1a; 矩阵的180度翻转是指将矩阵绕中心水平和垂直翻转。这个操作类似于将图像或矩阵上下颠倒&#xff0c;然后左右颠倒。翻转后的矩阵在水平和垂直方向上保持了原始矩阵的对称性。 让我们以…

【性能测试】老鸟总结,性能测试到底该如何做,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、前期准备 性能…

2023年第2季社区Task挑战赛贡献者榜单

基于FISCO BCOS及Weldentity&#xff0c;实现SSO单点登录服务&#xff1b;提供食品溯源、电商运费险7天退保、电子病历等智能合约库业务场景案例&#xff1b;基于FISCO BCOS更新游戏体验&#xff1b;体验并分析解读最新发布的分布式数据协作管理解决方案DDCMS&#xff0c;提供相…

Eviews 11安装包下载及安装教程

Eviews 11下载链接&#xff1a;https://docs.qq.com/doc/DUmRGdXVUeVBSU1lK 1.选中下载好的安装包&#xff0c;右键解压到“Eviews 11”文件夹 2.选中"Setup.exe"鼠标右键选择以管理员身份运行 3.点击“Next” 4.勾选“I Accept...”&#xff0c;点击“Next” 5.选择…

提升效率必备:用关键词替换法重命名文件夹技巧

在日常生活和工作中&#xff0c;经常要处理大量的文件夹&#xff0c;进行归类、整理和重命名。但是手动一个个重命名文件夹既费时又费力。为了提高效率&#xff0c;可以采用关键词替换法来批量重命名文件夹。现在讲解云炫文件管理器如何用关键词替换命名文件夹的具体步骤。 首先…

电视机顶盒哪个牌子好?2024电视机顶盒排行榜全新出炉

电视机顶盒可以解决电视机无法下载软件&#xff0c;配置落后卡顿等问题&#xff0c;用电视机顶盒资源丰富&#xff0c;功能多样&#xff0c;但超多朋友不知道电视机顶盒哪个牌子好&#xff0c;小编本期要分享的就是2024业内最新发布的电视机顶盒排行榜&#xff0c;入围的是以下…

构建Python随机密码生成器:保障账户安全的简易工具

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 密码安全是当前数字时代的一个重要议题。在保护…

实时记录和查看Apache 日志

Apache 是一个开源的、广泛使用的、跨平台的 Web 服务器&#xff0c;保护 Apache Web 服务器平台在很大程度上取决于监控其上发生的活动和事件&#xff0c;监视 Apache Web 服务器的最佳方法之一是收集和分析其访问日志文件。 Apache 访问日志提供了有关用户如何与您的网站交互…