侯捷C++面向对象编程(下)课程打卡 Day5

1 转换

1.1 转换函数

将当前对象的类型转换成其他类型

  • operator开头,函数名称为需要转换的类型,无参数
  • 前面无需写返回类型,编译器会自动根据函数名称进行补充
  • 转换函数中,分子分母都没有改变,所有通常加const\
// class Fraction里的一个成员函数
operator double() const
{
    return (double) (m_numerator / m_denominator);
}
Fraction f(3,5);
double d = 4 + f; //编译器自动调用转换函数将f转换为0.6
1.2 non-explicit-one-argument ctor

将其他类型的对象转换为当前类型

one-argument 表示只要一个实参就够了

// non-explicit-one-argument ctor
Fraction(int num, int den = 1) 
    : m_numerator(num), m_denominator(den) {}
Fraction f(3,5);
Fraction d = f + 4; //编译器调用ctor将4转化为Fraction
1.3 explicit

当上面两个都有转换功能的函数在一起,编译器调用时都可以用,报错

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) const
	{
		return Fraction(...);
	}
private:
	int m_numerator; // 分子
	int m_denominator; // 分母
};
...
    
Fraction f(3,5);
Fraction d = f + 4; // [Error] ambiguous

one-argument ctor 加上 explicit,表示这个 ctor 只能在构造的时候使用,编译器不能拿来进行类型转换

...
explicit Fraction(int num, int den = 1) 
    : m_numerator(num), m_denominator(den) {}
...
    
Fraction f(3,5);
Fraction d = f + 4; // [Error] 4不能从‘double’转化为‘Fraction’

关键字 explicit 主要就在这里运用

2 xxx-like classes

2.1 pointer-like classes
2.1.1 智能指针
  • 设计得像指针class,能有更多的功能,包着一个普通指针
  • 指针允许的动作,这个类也要有,其中 *-> 一般都要重载
template <typename T>
class shared_ptr
{
public:
	T& operator*() const { return *px; }
	T* operator->() const { return px; }
	shared_ptr(T* p) : ptr(p) {}
private:
	T* px;
	long* pn;
};

在使用时,*shared_ptr1 就返回 *px

但是 shared_ptr1-> 得到的东西会继续用 -> 作用上去,相当于这个->符号用了两次

image-20230807095542200

2.1.2 迭代器

标准库中的链表迭代器为例,这种智能指针还需要处理 ++ -- 等符号

node是迭代器包着的一个真正的指针,其指向_list_node

image-20230807100734372

  • 下图 *ite 的意图是取 data——即一个 Foo 类型的 object
  • 下图 ite->method 的意图是调用 Foo 中的函数 method

image-20230807100804223

2.2 function-like classes

设计一个class,行为像一个函数

函数行为即 —— xxx() 有一个小括号,所以函数中要有对 () 进行重载

template <class pair>
struct select1st ... // 这里是继承奇特的Base classes,先不管
{
	const typename pair::first_type& // 返回值类型,先不管
	operator()(const pair& x) const
	{
		return x.first;
	}
};

...
//像一个函数一样在用这个类
select1st<my_pair> selector;
first_type first_element = selector(example_pair);

//还可以这样写,第一个()在创建临时对象
first_type first_element = select1st<my_pair>()(example_pair);

...

3 模板

3.1 类模板/函数模板

补充:只有模板的尖括号中<>,关键字 typenameclass 是一样的

3.2 成员模板

它即是模板的一部分,自己又是模板,则称为成员模板

其经常用于构造函数

  1. ctor1 这是默认构造函数的实现;它初始化 first 和 second 分别为 T1 和 T2 类型的默认构造函数生成的默认值

  2. ctor2 这是带参数的构造函数的实现;它接受两个参数 a 和 b,并将它们分别用来初始化 first 和 second 成员变量

  3. ctor3 这是一个==模板构造函数==,接受一个不同类型的 pair 对象作为参数;它允许从一个不同类型的 pair 对象构造当前类型的 pair 对象,在构造过程中,它将源 pair 对象的 first 和 second 成员变量分别赋值给当前对象的成员变量,使其具有一定的灵活性和通用性。

template <class T1, class T2>
struct pair
{
	T1 first;
	T2 second;
	pair() : first(T1()), second(T2()) {} //ctor1
	pair(const T1& a, const T2& b) : 	  //ctor2
		first(a), second(b) {}

	template <class U1, class U2>		  //ctor3
	pair(const pair<U1, U2>& p) : 
		first(p.first), second(p.second) {}
};
  • 例一,可以使用 <鲫鱼,麻雀> 对象来构造一个 <鱼类,鸟类> 的pair

image-20230807152238567

  • 例二,父类指针是可以指向子类的,叫做 up-cast;智能指针也必须可以,所以其构造函数需要为==模板构造函数==

    image-20230807152501305

3.3 模板模板参数

即模板中的一个模板参数也为模板,下图黄色高亮部分

image-20230807161321152

  • XCLs<string, list> mylist 中即表示:容器 list 是 string 类型的—— 创建一个 string 的链表;Container c; 即表示 list c;
  • 但是这样 Container c; 语法过不了,容器 list 后面还有参数,需要用中间框和下面框下一行的代码 —— c++11的内容

注:下面不是模板模板参数

image-20230807162712548

class Sequence = deque<T>` 是有一个初始值,当没指定时就初始为 `deque<T>

在要指定时,如最后一行中的 list<int> 是确切的,不是模板