内存分配和管理、智能指针、强制类型转换运算符、运行时类型信息(RTTI)

文章目录

    • 内存分配和管理
      • malloc、calloc、realloc、alloca
      • malloc、free
      • new、delete
      • 定位new
    • delete this合法吗?
    • 如何定义一个只能在堆上(栈上)生成对象的类?
      • 只能在堆上
      • 只能在栈上
    • 智能指针
      • shared_ptr
      • weak_ptr
      • unique_ptr
      • auto_ptr
      • auto_ptr 与 unique_ptr 比较
    • 强制类型转换运算符
      • static_cast
      • dynamic_cast
      • const_cast
      • reinterpret_cast
      • bad_cast
    • 运行时类型信息(RTTI)

本文参考 博客,感谢博主
LeetCode屏蔽调了输入和输出处理,并给出了要实现的函数的描述,包括返回值类型、参数、参数类型。这就潜在地提供了很多关于测试数据的辅助信息。
LeetCode有些题目的测试数据比较宽容,对于练习算法和数据结构来说,很合适,因为AC题目不会被很多边界条件所打扰。

内存分配和管理

malloc、calloc、realloc、alloca

malloc:申请指定字节数的内存。申请到的内存中的初始值不确定
calloc:为指定长度的对象分配能容纳其指定个数的内存。申请到的内存的每一位(bit)都初始化为0
realloc:更改以前分配的内存长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定
alloca:在栈上申请内存。程序在出战的时候,会自动释放内存。但是需要注意的是,alloca不具有可移植性,而且在没有传统堆栈的机器上很难实现。alloca不宜使用在必须广泛移植的程序中。C99中支持变长数组(VLA),可以用来替代alloca。

malloc、free

用于分配、释放内存
malloc、free使用
申请内存,确认是否申请成功

char *str = (char *)malloc(100);
assert(str!=nullptr);

释放内存后指针置空

free(p);
p = nullptr;

new、delete

new/new[]:完成两件事,先底层调用malloc分配了内存,然后调用构造函数(创建对象)。
delete/delete[]:完成两件事,先调用析构函数(清理资源),然后底层调用free释放空间。
new在申请内存时会自动计算所需字节数,而malloc则需我们自己输入申请内存空间的字节数。
new、delete的使用

int main()
{
	T* t = new T();
	delete t;
	return 0;
}

定位new

定位new(placement new)允许我们想new传递额外的地址参数,从而在预先指定的内存区域创建对象。

new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] {braced initializer list}

place_address是个指针
initializers提供一个(可能为空的)以逗号分隔的初始化列表

delete this合法吗?

合法,但:
必须保证 this 对象是通过 new(不是 new[]、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的
必须保证调用 delete this 的成员函数是最后一个调用 this 的成员函数
必须保证成员函数的 delete this 后面没有调用 this 了
必须保证 delete this 后没有人使用了

如何定义一个只能在堆上(栈上)生成对象的类?

只能在堆上

方法:将析构函数设置为私有
原因:C++ 是静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。

只能在栈上

方法:将 new 和 delete 重载为私有
原因:在堆上生成对象,使用 new 关键词操作,其过程分为两阶段:第一阶段,使用 new 在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。将 new 操作设置为私有,那么第一阶段就无法完成,就不能够在堆上生成对象。

智能指针

C++标准库(STL)中
头文件:#include

C++98
std::auto_ptr ps (new std::string(str));

C++11
shared_ptr
unique_ptr
weak_ptr
auto_ptr(被C++11弃用)

Class shared_ptr 实现共享式拥有(shared ownership)概念。多个智能指针指向相同对象,该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作,标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。
Class unique_ptr 实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏(resource leak)——如 new 后忘记 delete ——特别有用。

shared_ptr

多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。
支持定制型删除器(custom deleter),可防范 Cross-DLL 问题(对象在动态链接库(DLL)中被 new 创建,却在另一个 DLL 内被 delete 销毁)、自动解除互斥锁

weak_ptr

weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。
可打破环状引用(cycles of references,两个其实已经没有被使用的对象彼此互指,使之看似还在 “被使用” 的状态)的问题

unique_ptr

unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有着被销毁或编程 empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。
unique_ptr 用于取代 auto_ptr

auto_ptr

被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 std::move 语义,以及其他瑕疵。

auto_ptr 与 unique_ptr 比较

auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了move 语义;
auto_ptr 对象不能管理数组(析构调用 delete),unique_ptr 可以管理数组(析构调用 delete[] );

强制类型转换运算符

static_cast

用于非多态类型的转换
不执行运行时类型检查(转换安全性不如 dynamic_cast)
通常用于转换数值数据类型(如 float -> int)
可以在整个类层次结构中移动指针,子类转化为父类安全(向上转换),父类转化为子类不安全(因为子类可能有不在父类的字段或方法)
向上转换是一种隐式转换。

dynamic_cast

用于多态类型的转换
执行行运行时类型检查
只适用于指针或引用
对不明确的指针的转换将失败(返回 nullptr),但不引发异常
可以在整个类层次结构中移动指针,包括向上转换、向下转换

const_cast

用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 )

reinterpret_cast

用于位的简单重新解释
滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
允许将任何指针转换为任何其他指针类型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之类的转换,但其本身并不安全)
也允许将任何整数类型转换为任何指针类型以及反向转换。
reinterpret_cast 运算符不能丢掉 const、volatile 或 __unaligned 特性。
reinterpret_cast 的一个实际用途是在哈希函数中,即,通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引。

bad_cast

由于强制转换为引用类型失败,dynamic_cast 运算符引发 bad_cast 异常。
bad_cast 使用

try{
	Circle& ref_circle = dynamic_cast<Circle&>(ref_shape);
}
catch(bad_cast b){
	cout<<"Caught: "<<b.what();
}

运行时类型信息(RTTI)

dynamic_cast
用于多态类型的转换
typeid
typeid 运算符允许在运行时确定对象的类型
type_id 返回一个 type_info 对象的引用
如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数
只能获取对象的实际类型
type_info
type_info 类描述编译器在程序中生成的类型信息。 此类的对象可以有效存储指向类型的名称的指针。 type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。 类型的编码规则和排列顺序是未指定的,并且可能因程序而异。
头文件:typeinfo
typeid、type_info 使用

#include
using namespace std;

class Flyable
{
public:
	virtual void takeoff() = 0;
	virtual void land() = 0;
};
class Bird : public Flyable
{
public:
	void foraging(){...}
	virtual void takeoff(){...}
	virtual void land(){...}
	virtual ~Bird(){}
};
class Plane : public Flyable
{
public:
	void carry(){...}
	virtual void takeoff(){...}
	virtual void land(){...}
};
class type_info
{
public:
	const char* name() const;
	bool operator == (const type_info & rhs) const;
	bool operator != (const type_info & rhs) const;
	int before(const type_info & rhs) const;
	virtual ~type_info();
private:
	...
};
void doSomething(Flyable *obj)
{
	obj->takeoff();
	
	cout<<typeid(*obj).name()<<endl;
	
	if(typeid(*obj)==typeid(Bird))
	{
		Bird *bird = dynamic_cast<Bird *>(obj);
		bird->foraging();
	}
	
	obj->land();
}

int main()
{
	Bird *b = new Bird();
	doSomething(b);
	delete b;
	b = nullptr;
	return 0;
}

你可能感兴趣的:(C++,c++)