既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
class type\_info
{
public:
virtual ~type\_info();
const char\* name() const _GLIBCXX_NOEXCEPT
{ return __name[0] == '\*' ? __name + 1 : __name; }
#if !\_\_GXX\_TYPEINFO\_EQUALITY\_INLINE
bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
#else
#if !\_\_GXX\_MERGED\_TYPEINFO\_NAMES
bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT
{ return (__name[0] == '\*' && __arg.__name[0] == '\*') ?
__name < __arg.__name : __builtin_strcmp (__name, __arg.__name) < 0;
}
bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
{
return ((__name == __arg.__name) ||
(__name[0] != '\*' && __builtin_strcmp (__name, __arg.__name) == 0));
}
#else
bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT
{ return __name < __arg.__name; }
bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
{ return __name == __arg.__name; }
#endif
#endif
bool operator!=(const type_info& __arg) const _GLIBCXX_NOEXCEPT
{ return !operator==(__arg); }
#if \_\_cplusplus >= 201103L
size_t hash\_code() const noexcept
{
# if !\_\_GXX\_MERGED\_TYPEINFO\_NAMES
return \_Hash\_bytes(name(), \_\_builtin\_strlen(name()),
static\_cast<size_t>(0xc70f6907UL));
# else
return reinterpret\_cast<size_t>(__name);
# endif
}
#endif // C++11
virtual bool \_\_is\_pointer\_p() const;
virtual bool \_\_is\_function\_p() const;
virtual bool \_\_do\_catch(const type_info \*__thr_type, void \*\*__thr_obj,
unsigned __outer) const;
virtual bool \_\_do\_upcast(const __cxxabiv1::__class_type_info \*__target,
void \*\*__obj_ptr) const;
protected:
const char \*__name;
explicit type\_info(const char \*__n): \_\_name(__n) { }
private:
type_info& operator=(const type_info&);
type\_info(const type_info&);
};
从源代码中可以看出以下几点内容:
- 有一个类成员
__name
,类型是const char*
,这个指针最终会指向类型的名字 - 我们不能直接实例化类
type_info
的对象,因为该类的正常构造函数是保护的,要构造type_info
对象的唯一方法就是使用typeid
运算符。 - 由于重载的赋值运算符和拷贝构造函数也是私有的,因此我们不能自己去复制或分配类
type_info
的对象。 - 其余那些成员方法大家就自己看一下吧,都是一些从名字就能看出用法的函数,比如
name()
返回类型名,__is_pointer_p()
返回是否是指针类型等等
2.2、typeid识别静态类型
当typeid
中的操作数是以下任意一种时,typeid
得出的是静态类型,即编译时就确定的类型:
- 一个任意的类型名
- 一个基本内置类型的变量,或指向基本内置类型的指针或引用
- 一个任意类型的指针(指针就是指针,本身不体现多态,多指针解引用才有可能会体现多态)
- 一个具体的对象实例,无论对应的类有没有多态都可以直接在编译器确定
- 一个指向没有多态的类对象的指针的解引用
- 一个指向没有多态的类对象的引用
由于静态类型在程序的运行过程中并不会改变,所以并不需要等到程序运行时再去推算其类型,在编译时期就能根据操作数的静态类型,从而推导出其具体类型信息。我们先来看如下一段代码:
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
int main(int argc, char\* argv[])
{
std::cout << typeid(char).name() << std::endl;
std::cout << typeid(int).name() << std::endl;
std::cout << typeid(double).name() << std::endl;
std::cout << "----------" << std::endl;
std::cout << typeid(unsigned char).name() << std::endl;
std::cout << typeid(unsigned int).name() << std::endl;
std::cout << "----------" << std::endl;
std::cout << typeid(std::string).name() << std::endl;
std::cout << typeid(std::vector<float>).name() << std::endl;
}
运行结果如下(gcc-4.8.5):
c
i
dh
jSs
St6vectorIfSaIfEE
看完我有点怀疑我的智商和人生,不是,是码生了,前面几个还好,还能大概看出是什么类型,但是后面那几个是什么鬼,尤其是这个St6vectorIfSaIfEE
,谁能看出是std::vector<float>
了?这些其实是被编译器转换过后的类型名,typeid
也只能获取这种形式的名称。由于直接看这个实在有点反人类,所以我们只能动用点非常手段了,修改代码如下:
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
#include <cxxabi.h>
const char\* TypeToName(const char\* name)
{
const char\* __name = abi::\_\_cxa\_demangle(name, nullptr, nullptr, nullptr);
return __name;
}
int main(int argc, char\* argv[])
{
std::cout << TypeToName(typeid(char).name()) << std::endl;
std::cout << TypeToName(typeid(int).name()) << std::endl;
std::cout << TypeToName(typeid(double).name()) << std::endl;
std::cout << "----------" << std::endl;
std::cout << TypeToName(typeid(unsigned char).name()) << std::endl;
std::cout << TypeToName(typeid(unsigned int).name()) << std::endl;
std::cout << "----------" << std::endl;
std::cout << TypeToName(typeid(std::string).name()) << std::endl;
std::cout << TypeToName(typeid(std::vector<float>).name()) << std::endl;
}
运行结果如下,现在可以看到的内容就正常多了,这里其实就是用到了abi::__cxa_demangle
这个方法获取类型的真实名称,关于这部分的内容这里就不细讲了,有兴趣的读者可以去看这篇文章《C++封装一个易用的打印backtrace信息的函数》,里面就有相应的介绍。
char
int
doubleunsigned char
unsigned intstd::string
std::vector<float, std::allocator >
有了前面小小的铺垫,我们可以着手编写测试代码了:
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
#include <cxxabi.h>
const char\* TypeToName(const char\* name)
{
const char\* __name = abi::\_\_cxa\_demangle(name, nullptr, nullptr, nullptr);
return __name;
}
class A
{
public:
void print()
{
std::cout << "A" << std::endl;
}
};
class B : public A
{
public:
void print()
{
std::cout << "B" << std::endl;
}
};
class C
{
public:
virtual void print()
{
std::cout << "C" << std::endl;
}
};
class D : public C
{
public:
virtual void print()
{
std::cout << "D" << std::endl;
}
};
int main(int argc, char\* argv[])
{
std::cout << "---------- 一个任意的类型名 ----------" << std::endl;
std::cout << "----- 基本内置类型名 -----" << std::endl;
std::cout << TypeToName(typeid(char).name()) << std::endl;
std::cout << TypeToName(typeid(int).name()) << std::endl;
std::cout << "----- 无多态的类型名 -----" << std::endl;
std::cout << TypeToName(typeid(std::string).name()) << std::endl;
std::cout << TypeToName(typeid(std::vector<int>).name()) << std::endl;
std::cout << "----- 有多态的类型名 -----" << std::endl;
std::cout << TypeToName(typeid(std::iostream).name()) << std::endl;
std::cout << "\n\n" << "---------- 一个基本内置类型的变量,或指向基本内置类型的指针或引用 ----------" << std::endl;
std::cout << "----- 基本内置类型的变量 -----" << std::endl;
long type_long = 1;
double type_double = 1.23;
std::cout << TypeToName(typeid(type_long).name()) << std::endl;
std::cout << TypeToName(typeid(type_double).name()) << std::endl;
std::cout << "----- 指向基本内置类型的指针或引用 -----" << std::endl;
long\* type_long_ptr = &type_long;
double& type_double_ref = type_double;
std::cout << TypeToName(typeid(type_long_ptr).name()) << std::endl;
std::cout << TypeToName(typeid(type_double_ref).name()) << std::endl;
std::cout << "\n\n" << "---------- 一个任意类型的指针 ----------" << std::endl;
std::cout << "----- 一个指向没有多态类型的指针 -----" << std::endl;
B b;
A\* a_ptr = &b;
std::cout << TypeToName(typeid(&b).name()) << std::endl;
std::cout << TypeToName(typeid(a_ptr).name()) << std::endl;
std::cout << "----- 一个指向具有多态类型的指针 -----" << std::endl;
D d;
C\* c_ptr = &d;
std::cout << TypeToName(typeid(&d).name()) << std::endl;
std::cout << TypeToName(typeid(c_ptr).name()) << std::endl;
std::cout << "\n\n" << "---------- 一个具体的对象实例,无论对应的类有没有多态都可以直接在编译器确定 ----------" << std::endl;
std::cout << "----- 无多态类的实例 -----" << std::endl;
std::string type_string;
std::vector<int> type_vector;
std::cout << TypeToName(typeid(type_string).name()) << std::endl;
std::cout << TypeToName(typeid(type_vector).name()) << std::endl;
std::cout << "----- 有多态类的实例 -----" << std::endl;
std::iostream type\_ios(nullptr);
std::cout << TypeToName(typeid(type_ios).name()) << std::endl;
std::cout << "\n\n" << "---------- 一个指向没有多态的类对象的指针的解引用 ----------" << std::endl;
std::cout << "----- 一个指向没有多态的类对象的指针的解引用 -----" << std::endl;
std::cout << TypeToName(typeid(\*a_ptr).name()) << std::endl; // 推算出来的依然是A
std::cout << "\n\n" << "---------- 一个指向没有多态的类对象的引用 ----------" << std::endl;
std::cout << "----- 一个指向没有多态的类对象的引用 -----" << std::endl;
A& a_ref = b;
std::cout << TypeToName(typeid(a_ref).name()) << std::endl; // 推算出来的依然是A
}
运行结果如下,前面讲的六种静态类型都涉及到了,大家看一下就能明白是怎么一回事了:
---------- 一个任意的类型名 ----------
----- 基本内置类型名 -----
char
int
----- 无多态的类型名 -----
std::string
std::vector<int, std::allocator >
----- 有多态的类型名 -----
std::iostream---------- 一个基本内置类型的变量,或指向基本内置类型的指针或引用 ----------
----- 基本内置类型的变量 -----
long
double
----- 指向基本内置类型的指针或引用 -----
long*
double---------- 一个任意类型的指针 ----------
----- 一个指向没有多态类型的指针 -----
B*
A*
----- 一个指向具有多态类型的指针 -----
D*
C*---------- 一个具体的对象实例,无论对应的类有没有多态都可以直接在编译器确定 ----------
----- 无多态类的实例 -----
std::string
std::vector<int, std::allocator >
----- 有多态类的实例 -----
std::iostream---------- 一个指向没有多态的类对象的指针的解引用 ----------
----- 一个指向没有多态的类对象的指针的解引用 -----
A---------- 一个指向没有多态的类对象的引用 ----------
----- 一个指向没有多态的类对象的引用 -----
A
2.3、typeid识别动态类型
当typeid
中的操作数是以下任意一种时,typeid
需要在程序运行时推算类型,因为其操作数的类型在编译时期是不能被确定的:
- 一个指向含有多态的类对象的指针的解引用
- 一个指向含有多态的类对象的引用
下面先来看一个典型错误案例,程序如下:
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
#include <cxxabi.h>
const char\* TypeToName(const char\* name)
![img](https://img-blog.csdnimg.cn/img_convert/827f28308799da8ae70ba3655db1f399.png)
![img](https://img-blog.csdnimg.cn/img_convert/1fad681ea209c2fa302476f32736e04e.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
einfo>
#include <cxxabi.h>
const char\* TypeToName(const char\* name)
[外链图片转存中...(img-IdZW6tbb-1715597730437)]
[外链图片转存中...(img-Mm0Wg39x-1715597730437)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**