C++ 语法学习

本文介绍了C++中的运行时类型识别(RTTI)机制,包括`type_info`类和`typeid`操作符的使用,以及如何通过`type_info`获取对象的类型信息。此外,还探讨了`std::optional`在处理可选值中的作用,提高代码可读性和错误处理能力。最后,文章提到了阻止类继承的三种方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的构造函数,该方法就失效了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值