侯捷 C++学习笔记之面向对象高级编程(二)
侯捷 C++ 学习笔记之面向对象高级编程(二)
conversion function 转换函数
- 转换函数(分数转为
double
)程序示例:
1 |
|
-
说明:对于上述示例的转换函数,可以看到其相当于操作符重载(
double
属于显式类型转换运算符),该转换函数不需要返回类型,因为本身转换为double
类型,属于C++
语言特性,能够避免返回类型与转换类型不一致的问题。 -
上述示例的调用示例如下:
1 |
|
non-explicit-one-argument 构造函数
one-argument
是指该构造函数只需要一个实参就足够了,其中以下示例的构造函数有den=1
这个默认参数,因此是属于one-argument
的。
1 |
|
- 上述示例的调用示例如下:
1 |
|
explicit(明确的) 构造函数
- 将上述两种方式组合一下可能会造成歧义,来看下述例子:
1 |
|
- 使用示例:
1 |
|
- 存在两种可能性:
- 一是将
4
转为Fraction
类型进行相加; - 二是将
f
转为double
类型进行相加。
- 一是将
- 两种错误就会导致编译器报错,提示 ambiguous(存在歧义的),解决方法是在构造函数前加入
explicit
关键字,如下示例:
1 |
|
- 使用示例:
1 |
|
- 说明:加入
explicit
关键字后说明不让编译器自动将4
转为Fraction
类型。因此示例的第一个使用方法会报错,提示需要从double
类型转为Fraction
类型。而第二个使用方法能够将f
转为double
类型并相加就不会报错。
pointer-like classes
智能指针
- 较为简单的智能指针的示例(
shared_ptr
):
迭代器
- 迭代器作为容器中的一种用法,以链表迭代器为例:
注意点
- 关于
C++
语言中的指针,需要注意运算符.
和->
用法的区别,例子如下:
1 |
|
- 输出结果:
- 从上述输出结果中不难看出,
(*name).size()
与name->size()
的输出结果一致,使用的是string
这个对象里面的size()
函数。 - 而
name.use_count()
输出的是数值 1,使用的是智能指针shared_ptr<string>
里面的use_count()
函数。 use_count()
函数表示有多少个智能指针指向某个对象,主要用于调试目的。例如:
1 |
|
function-like classes,仿函数
- 在 C++中,函数调用运算符(
operator()
)允许一个类的对象表现得像函数一样被调用。这种特性通过重载类的圆括号操作符operator()
实现。 - 注:小括号
()
就是函数调用操作符,operator()
就是对函数调用操作符进行重载。 - 这对于实现类似于函数的对象非常有用,例如,你可以创建一个对象,该对象封装了一组操作,而这些操作可以通过调用对象来实现。
- 例如创建一个简单的计算器类,该类可以执行加法和减法操作:
1 |
|
function template,函数模板
- 编译器会对函数模板(
function template
)进行实参推导 (argument deduction
)。
member template,成员模板
- 在模板类里面还有一份模板,成为成员模板:
- 成员模板中的继承关系:
specialization,模板特化
- 全特化:特定类型的模板执行特定类型的函数或事情。
模板的偏特化
- 个数的偏:
- 范围的偏:
template template parameter,模板模板参数
- 模板模板参数,即允许一个模板接受另一个模板作为参数。
- 可以使用
template<template<...> class...>
语法来定义一个模板模板参数。
- 不是模板模板参数的情况:
C++ 11 新特性三个主题简介
variadic templates,数量不定的模板参数
- 数量不定的模板参数的语法是在模板声明中使用省略号(
...
)来表示参数的数量是不确定的。例如:
1 |
|
- 完整代码示例:
1 |
|
- 可以用
sizeof... (args)
来获取args
内具多少元素:
auto
C++
中的auto
关键字主要用于自动推导变量的类型。在C++ 11
及以后的版本中,auto
不再表示存储类型,而是作为一个类型指示符,用于指示编译器自动推导变量的类型。使用auto
可以简化代码,尤其是在处理复杂类型时,能够减少代码量并提高可读性。
ranged-base for,基于范围的 for 循环
- 基于范围的
for
循环是一种简洁而强大的语法,用于遍历容器(如数组、向量、列表、集合等)。
reference,再谈引用
object
和其reference
的大小相同,地址也相同。
- 引用(
reference
)通常不用于声明变量,而用于参数类型(parameters type
)和返回类型(return type
)的描述。
对象模型(Object Model)
关于虚指针(vptr)和虚函数表(vtbl)
- 在 C++中,虚函数和多态是通过虚指针(
vptr
)和虚函数表(vtbl
)来实现的。这种机制允许在运行时根据对象的实际类型来调用相应的函数,而不是在编译时确定。这对于实现多态行为非常关键,比如在基类指针或引用调用派生类的方法时。 - 虚指针(
vptr
):- 每个包含虚函数的类都会有一个或多个虚指针(
vptr
); - 虚指针是一个指向虚函数表的指针,该表包含了指向各个虚函数的指针。这个表对于类的每一个对象来说是唯一的,确保了多态性的实现。
- 每个包含虚函数的类都会有一个或多个虚指针(
- 虚函数表(
vtbl
):- 虚函数表是一个静态的数据结构,它存储了类中所有虚函数的地址;
- 对于每个包含虚函数的类,编译器都会生成一个虚函数表,当类的对象被创建时,其虚指针(
vptr
)被初始化为指向该类的虚函数表的地址。
- 代码示例:
1 |
|
-
在这个例子中,
Base
类有一个虚函数func
。当Derived
类继承Base
并重写func
函数时,编译器会做以下事情:-
为
Base
类生成一个虚函数表,其中包含func
函数的地址。 -
为
Derived
类生成一个新的虚函数表,其中包含func
函数的地址(即Derived
类中的新实现)。 -
在
Derived
类的每个对象中,其虚指针(vptr
)被初始化为指向Derived
的虚函数表。
-
-
访问和调用过程:当通过基类指针调用虚函数时,实际调用的函数取决于对象的实际类型。
1 |
|
- 在这个例子中,尽管
b
是Base
类型的指针,但由于b
指向一个Derived
类型的对象,所以调用的是Derived
的func
方法。这是因为编译器会根据对象的实际类型(通过其虚指针找到正确的虚函数表),从而确定并调用正确的函数。 - 图片示例:
多态
- 多态(
Polymorphism
)是面向对象编程中的一个核心概念,允许一个接口被多种不同的实现使用。在 C++中,多态主要通过虚函数(virtual functions
)和继承来实现。
虚函数
- 在 C++中,虚函数允许你在派生类中重写基类的函数。这是实现多态的基础:
1 |
|
指针和引用的多态性
- 通过基类指针或引用调用虚函数时,会根据对象的实际类型来调用相应的函数,这称为动态绑定(
Dynamic Binding
)。
1 |
|
纯虚函数和抽象类
- 纯虚函数是一种特殊的虚函数,它在基类中没有实现,要求任何继承该类的子类必须提供具体实现。含有纯虚函数的类是抽象类,不能直接实例化。
1 |
|
关于 this 和动态绑定(Dynamic Binding)
- 动态绑定(
Dynamic Binding
),this
指针所指的对象(Object
)称为this Object
; - 虚函数的执行步骤,属于设计模式中的模板方法(
Template Method
):
-
动态绑定需要满足三个条件:
- 通过指针调用;
- 向上转型的动作(如
this
指针的对象是子类对象); - 调用
this
指针的子类对象的虚函数。
-
静态绑定:
- 动态绑定:
谈谈 const
- 常量成员函数:
- 一个函数通过在其后面加关键字
const
,它将被声明为常量函数; - 在 C++,只有将成员函数声明为常量函数才有意义。带有
const
作后缀的常量成员函数又被称为视察者 (inspector
),没有const
作后缀的非常量成员函数被称为变异者 (mutator
); - 常量函数可以被任何对象调用,而非常量函数则只能被非常量对象调用,不能被常量对象调用;
- 在 C++中,一个对象的所有方法都接收一个指向对象本身的隐含的
this
指针,常量方法则获取了一个隐含的常量this
指针; - 在类中允许存在同名的常量函数和非常量函数,编译器根据调用该函数的对象选择合适的函数:
- 当非常量对象调用该函数时,先调用非常量函数;
- 当常量对象调用该函数时,只能调用常量函数;
- 如果在类中只有常量函数而没有与其同名的非常量函数,则非常量与常量对象都可调用该常量函数。
关于 new 和 delete
- 第一部分关于
new
和delete
的汇总:
- 重载全局
new
和全局delete
以及全局new[]
和全局delete[]
:
- 重载成员
new
和成员delete
:
- 重载成员
new[]
和成员delete[]
:
- 设计示例和接口:
- 重载
new()
、delete()
:
- 标准库重载
new()
的示例:
侯捷 C++学习笔记之面向对象高级编程(二)
http://example.com/2025/02/28/houjie_cpp_2/