C语言类型转换

C语言总共有两种形式的类型转换:隐式类型转换 和 显示类型转换。
C语言的转换格式虽然很简单,但也存在不少缺陷:

  1. 隐式类型转换有些情况下可能会引发意料之外的结果,比如数据精度丢失。
  2. 显示类型转换的可视性比较差,它将所有转换的情况都混合在一起,使代码不够清晰。

C++之所以还要提出自己的类型转换,主要是为了更好地规避C语言风格类型转换所带来的的缺陷和风险。

C++类型转换

  • static_cast
    static_cast相当于C语言中的隐式类型转换,用于意义相近的类型。
  • reinterpret_cast
    reinterpret_cast用于将一种类型转换为另一种类型。
  • const_cast
    const_cast通常用于删除变量的const属性,以方便赋值。

reinterpret_castconst_cast都是C语言角度下的强制类型转换。

void Test1()
{
	const int a = 2;
	int* p = const_cast<int*>(&a);

	*p = 3;

	cout << a << endl;
	cout << *p << endl;
}

【C++】类型转换 | IO流 | 空间配置器-LMLPHP
上面的输出结果不一样,这是由于const变量作为常变量,在使用的地方可能预处理阶段就被替换成了常量;或者说编译器是将const变量存储在寄存器中等特殊处理的结果。要想避开这种处理,让const变量保持以内存存储的形式,可以使用volatile关键字:volatile const int a = 2;
【C++】类型转换 | IO流 | 空间配置器-LMLPHP

dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针/引用(父类对象是无论如何都不能转换成子类对象)。
向上转型:子类对象指针/引用 --> 父类指针/引用(不需要转换,本来就赋值兼容);
所有的类型转换都会出现临时变量,而向上转型并不会有临时变量产生,所以向上转型本质并不属于类型转换。
向下转型:父类对象指针/引用 --> 子类指针/引用(用dynamic_cast转型)。

class A
{
public:
	// dynamic_cast只能用于父类含有虚函数的类
	virtual void f() {}
public:
	int _a = 0;
};

class B : public A
{
public:
	int _b = 1;
};

// pa可能指向父类,也可能指向子类
void fun(A* pa)
{
	// 如果pa指向子类,那么可以转换,转换表达式返回正确的地址
	// 如果pa指向父类,那么不能转换,转换表达式返回nullptr
	B* pb = dynamic_cast<B*>(pa);
	if (pb)
	{
		cout << "转换成功" << endl;
		cout << pb->_a << pb->_b << endl;
	}
	else
	{
		cout << "转换失败" << endl;
		cout << pa->_a << endl;
	}
}

void Test2()
{
	A a1;
	B b1;
	fun(&a1);
	fun(&b1);
}

【C++】类型转换 | IO流 | 空间配置器-LMLPHP

RTTI

RTTI(Run-time Type identification),即运行时类型识别。
C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
  3. decltype

IO流

“流”即流动的意思,是物质从一处向另一处流动的过程,是对一种连续有序且有方向性的数据的抽象描述。
为了实现IO流,C++实现了一个庞大的IO标准类库。
【C++】类型转换 | IO流 | 空间配置器-LMLPHP
库中提供了4个全局流对象cincoutcerrclog
cincout可以直接输入和输出内置类型数据,是因为库中已经将所有内置类型的输入和输出进行了重载。
对于自定义类型,如果想要支持cincout的输入输出,就需要自行对>><<进行重载。
如果想要实现循环输入,需要在istream中重载operator bool

istream& operator>> (type& val);
explicit operator bool() const;
class A
{
public:
	A(int a)
		: _a(a)
	{}

	explicit operator int()
	{
		return _a;
	}
private:
	int _a;
};

void Test1()
{
	// 内置类型转换成自定义类型
	A a = 1;

	// 自定义类型转换成内置类型
	int i1 = (int)a;
	int i2 = static_cast<int>(a);
}

cin去读取数据时,调用的是operator>>,返回的istream类型的对象。如果想要判断是否读取成功,则需要通过operator bool来判断。
stringstream类型的对象,在进行多次数据类型转换时,一定要用clear来清空,才能正确地转化。
clear不会将stringstream底层的string对象清空,可以使用str("")方法将底层string对象设置为""空字符串。

空间配置器

空间配置器,就是用于为各个容器高效地管理空间(空间的申请与回收)的。
空间配置器相比用户自己申请空间,主要优势在于,效率更高且能一定程度缓解内存碎片问题。
SGI版本的空间配置器设计中,对申请空间的大小做了一个划分。以128byte作为分界线,分别设计了一级空间配置器(处理大块内存申请)和二级空间配置器(处理小块内存申请)。
对于二级空间配置器,采用了内存池的技术来提高申请空间的速度并减少额外的空间浪费,采用哈希桶的结构来提高用户获取空间的速度并做高效的管理。
所谓内存池就是先申请一块大的内存块,当用户需要内存时,直接去内存池中去取即可。直到内存池中的空间不足时,才再次去向系统索取(大块内存)。当用户使用的内存不再需要,直接返回给内存池即可。这样的设计避免了用户频繁向系统申请小块内存所导致的效率低下,内存碎片问题。

11-11 21:17