1. type_info
C++的多态是面向对象编程的精髓之一, 但是在实际使用和调试中,很难知道一个指针绑定了哪个子类的对象,这被称为RTTI(运行时类型识别)。常常是读着读着代码,读到了一个 p->func() 就不知道到底在哪里实现的了。这时候type_info 和 typeid就派上用场了,它可以知道指针具体指向的类型,注意这只在多态的时候有用,也就是说基类没有虚函数这个方法会失效。
type_info类在头文件<typeinfo>
中定义,代表了一个C++类型的相关信息。一般由typeid操作符返回,不能自己构造。
type_info有下列方法:
name(),返回类型的名字
hash_code(),返回这个类型的哈希值(具有唯一性)
before(),可以判断一个type_info对象的顺序是否在另一个之前(实现相关,同一个程序多次调用都可能不一样,不太理解有什么实际作用)
==和!=操作符,判断两个type_info相等或不等
typeid操作符
typeid操作符在<typeinfo>
中声明,用来在运行时获取类型、变量、表达式的类型信息,适用于C++基础类型、内置类、用户自定义类、模板类等。
它有两种形式:
- typeid( 类型 )
- typeid( 表达式 )
#include<iostream>
#include<typeinfo>
using namespace std;
class B{
public:
virtual void v_fun(){};
void fun1() {};
void fun2() {};
virtual ~B(){};
};
class D : public B{
};
int main()
{
B* b1 = new B();
D d1;
B* b2 = &d1;
cout<<typeid(*b1).name()<<endl;
cout<<typeid(*b2).name()<<endl;
}
可以打印出 b2绑定的是D类型的对象,这在多态时非常有用,可以知道接口是由谁实现的。
2. optional
直面意思就是可选值,可以区分一个变量是否被赋值。 主要的作用是 代替各种未定义值,统一实现,例如:
当find查找一个字符串是否在一个string里,如果没找到会返回 std::string::npos, 而这个值的实际定义为 -1, 因此将find的返回值直接与-1比较也是可以的,这样就会使代码的可读性变差。如果使用迭代器找不到值,就会返回end_iterator,使用者就需要判断返回值是否等于尾迭代器。
optional的出现是为了统一定义这样一个 未定义值或者无效值, 它本质上是由类似 union的结构体构成,对象与一个指示是否是无效值的私有变量共享同一个内存空间。下面是使用optional代替默认值的一个例子
std::optional<int> binary_search(const std::vector<int> &list, int item) {
size_t low{0};
size_t high{list.size() - 1};
while (low <= high) {
auto mid = (low + high);
auto guess = list[mid];
if (guess == item) {
return mid;
} else if (guess > item) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return std::nullopt;
}
auto found = binary_search(list, 2);
if (found) {
std::cout << "found " << *found ; //可使用*found取值
}
if (found.has_value()) {
std::cout << "found " << found->value(); //使用成员函数value取值
}
if (found != std::nullopt) {
std::cout << "found " << (*found).value();
}
上面的二分查找中,我们使用optional作为函数的返回值,由于模板中实现了 对象 到 optional的转换函数,因此我们只要直接返回原始数据即可,当需要返回无效值时,返回std::nullopt。
在使用查询结果时,首先进行判断,如果其不为std::nullopt,则再去使用解引用或者value函数进行取值。
可以认为optional 一次性返回了两个变量,一个是本次查询成功与否,另一个是查询结果,而使用者不需要知道失败条件下返回的到底是什么,这在很多需要额外返回bool类型标志函数是否失败的地方会很有用。
3 使一个类不能被继承的方法
1) 使用final 关键词
2)声明私有构造函数, 利用静态方法进行构建
3) 利用模板和虚函数
template<typenameT>class MakeFinal{
friendT;
private:
MakeFinal(){}
~MakeFinal(){}
};
class FinalClass: virtual public MakeFinal<FinalClass> {
public:
FinalClass(){}
~FinalClass(){}
}
这个模板声明私有的构造函数,但让T作为其友元类,因此T类可以访问到其构造函数
但T类的子类不是MakeFinal的友元,因此不能访问到构造函数,注意这里必须是虚继承。无论在堆上还是在栈上 FinalClass都可以正常创建出来,但其不可以被继承
一般继承和基类 是 is A 的关系
虚继承和基类 是 has A 的关系,其内部持有一个虚基类表的指针,并没有实际的继承关系
所以当上述 MakeFinal 是虚继承的情况下,FinalClass只是持有了 MakeFinal 的一个指针。
当有其他类继承FinalClass时,由于子类与MakeFinal不存在继承关系,也不是其友元,所以无法调用其私有的构造函数,继承失败,编译出错。
当上述继承是普通继承的情况下,新的子类就会放到继承链里,可以访问到MakeFinal的构造函数,该方法就失效了。