2_5 new与delete、智能指针(share_ptr、weak_ptr、unique_ptr、auto_ptr)

5_1_new_delete.cpp

#include "hjcommon.hpp"

using namespace std;
HJ_NS_USING

class A
{
public:
	int m_num;
	A() : m_num(0) { cout << "no arg." << endl; }
	A(int num) : m_num(num) { cout << "one arg." << endl; }
	A(A *a) : m_num(a->m_num+1) { cout << "point arg." << endl; }
	A(const A &a) : m_num(a.m_num+4) { cout << "copy arg." << endl; }
	A(A &&a) noexcept : m_num(a.m_num+9) { cout << "move arg." << endl; }

	 // A *pa=new A[2];时,若A没自定义析构函数,那么可使用 delete pa; 来释放A的数组,如果定义了,那么new[]的时候编译器会多分配4个字节保存数组长度2,释放时也必须用delete[]
	~A() { cout << "~A." << endl; }
};

int main_2_5_1(int argc, char *argv[])
{
	// new auto()
	String *str = new String(5, 'a');
	auto s = new auto(str); // new auto(str); 等价于 String **s=new String*(str); ,接收用 auto、auto*、auto** 都可以,变量都是二级指针类型
	auto **ss = new auto(str); // 与 s 是一样的
	cout << hex << str << "," << *s << "," << *ss << endl; // 0x563b0c306290,0x563b0c306290,0x563b0c306290 ,指向同一块地址
	cout << (*s)->c_str() << ", " << (*ss)->c_str() << endl;
	delete str;
	delete s, ss; // 删除二级指针
	A *a = new A(1);
	auto aa = new auto(a); // 不会调用A的构造函数,所以证实 new auto(str); 等价于 String **s=new String*(str);
	// 0x559aac9aa710,0x559aac9aa710,a.m_num=1,aa.m_num=1
	cout << hex << a << "," << *aa << dec << ",a.m_num=" << a->m_num << ",aa.m_num=" << (*aa)->m_num << endl;
	delete a, aa; //  删除一级、二级指针
	// new const type()
	const int *pi = new const int(1); // 也可以 const int *pi = new int(1); 后面不加const
	// new[] 和 delete[]
	int *di = new int[10]; // 堆区int数组
	di[2] = 22;
	cout << *pi << ", " << di[2] << endl;
	delete pi, di;
	A *pda = new A[2](); // 动态分配堆区类数组,()可加可不加,类需要有无参的构造函数,且new的时候所有元素A都会调用无参构造函数完成初始化
//	pda++; delete pda; // 由于new的是A的数组,如果想单独delete下标0后的元素,运行会抛宕。。
//	pda = new A; // 重新new A赋值的话,不管数组指针是否移动到非0下标的元素位置,都不能使用 delete[] 来删除,否则会抛宕。。
	delete[] pda;

	// operator new() 和 operator delete() 建议还是直接使用 new和delete
	int *pv = (int *)operator new(100); // (int *)operator new(size)  表示动态分配size长度的int数组? 还是分配一个int然后赋值为100?
	pv[990] = 1011;
	cout << pv[0] << ", " << pv[990] << endl; // 0, 1011
	operator delete(pv); // 删除数组pv,只有一个int的时候也是这么删除?

	return 0;
}

5_2_智能指针.cpp

#include "hjcommon.hpp"
#include <memory>

using namespace std;
HJ_NS_USING

static shared_ptr<int> getSptr(int val)
{
	return shared_ptr<int>(new int(val));
}

int main_2_5_2(int argc, char *argv[])
{
	// 裸指针: int *pi=new int; 这种接收直接new出来的指针pi,就称为裸指针
	// 智能指针:就是为了解决裸指针可能带来的各种问题。 (忘释放。。或野指针等。) ,不需要再手动delete
	// c++标准库提供四种智能指针(std::): (都是类模板,需要 #include <memory>)
	// 1) auto_ptr (c++98): 已完全被 unique_ptr 取代,c++11中已弃用,建议不要使用
	// 2) unique_ptr (c++11): 独占式指针,同一时间只能有一个指针指向该对象
	// 3) shared_ptr (c++11): 共享式指针,多个shared_ptr指针指向同一个对象,最后一个shared_ptr指针析构或指向其他对象时,这个对象会被释放
	// 4) weak_ptr (c++11): 是辅助 shared_ptr 工作的

	shared_ptr<int> pi(new int(10)); // 可以
//	shared_ptr<int> pi2 = new int(10); // 不可以,shared_ptr构造函数用explicit修饰的,不能隐式类型转换
	shared_ptr<int> pii = pi; // 可以,调用拷贝构造函数
	shared_ptr<int> pi3 = getSptr(10); // get
	shared_ptr<int> pi4; // 不初始化时,默认指向nullptr

	// make_shared<type>() : std:: 标准库中高效创建shared_ptr的函数模板(提倡用此函数生成shared_ptr),支持类型type所有构造函数形参方式的传参
	shared_ptr<String> pStr = make_shared<String>(5, 'a');
	cout << pStr->c_str() << endl;
	auto p = make_shared<int>();
	p = make_shared<int>(10); // 重新指向新对象前会释放原对象内存。 不要 p = new int; ,智能指针与裸指针不要混用

	return 0;
}

5_3_shared_ptr.cpp

#include "hjcommon.hpp"
#include <memory>

using namespace std;
HJ_NS_USING

class A
{
public:
	int num = 0;
	A() { cout << "A." << endl; }
	~A() { cout << "~A." << endl; }
};

//template<typename T> // 不能用函数模板作删除器。。
static void myDelete(int *t) // 自定义删除器函数
{
	cout << "删除器函数" << endl;
	delete t;
}

int main_2_5_3(int argc, char *argv[])
{
	// shared_ptr 引用计数的增加:当发生拷贝构造函数或拷贝赋值运算符的时候,引用计数就会+1
	shared_ptr<int> p1 = make_shared<int>(10);
	auto p2(p1); // 拷贝构造函数, 这时 p1与p2 中的引用计数都是2
	// shared_ptr 引用计数的减少:当shared_ptr发生析构或指向别的对象时,引用计数就会-1
	auto p3 = make_shared<int>(10);
	p1 = p3; // 这时 p2中的引用计数会-1, p3的引用计数会+1,p2的与p3的引用计数相同

	// shared_ptr 常用函数:
	int count = p1.use_count(); // use_count() 返回引用计数
	cout << count << ", " << p1.unique() << endl; // 当use_count返回1时,unique()为真,其他都为假
	shared_ptr<int> p4; // 指向空时,若使用 *p4 会抛宕
	p4.reset(); // reset()会将指向对象的引用计数-1并将p4置空,当减后的引用计数为0时会释放该对象,若p4本来的引用计数为0(指向nullptr),reset()函数无作用
	p4.reset(new int(99)); // reset带上一个对象参数时,作用与不带参类似,唯一不同是p4会指向该对象,同时该对象的引用计数+1
	cout << *p4 << ", " << p4.use_count() << endl;
	int *pi = p4.get(); // 获取智能指针保存的裸指针,注意小心使用
	cout << *pi << endl;

	auto ps1 = make_shared<String>("some1");
	auto ps2 = make_shared<String>("some2");
	auto ps3(ps2);
	std::swap(ps1, ps2); // 交换裸指针,引用计数也会交换。 也可以 ps1.swap(ps2); ps2.swap(ps1);
	cout << *ps1 << "," << *ps2 << "," << *ps3 << "," << ps1.use_count() << "," << ps2.use_count() << endl; // some2,some1,some2,2,1

//	A a;
//	if (a) cout << "a true" << endl; // 对象不能作为条件判断表达式,编译会报错。
//	else cout << "a false" << endl;
	shared_ptr<int> p5(new int(5));
	auto pp5(p5);
	p5 = nullptr; // 作用与无参reset() 一样
	cout << *pp5 << endl; // 5
	if (p5) cout << "true" << endl; // 智能指针的对象却可以作为条件判断表达式。。 编译器作了手脚?
	else cout << "false" << endl; // false

	// 指定删除器,默认删除器就只有一个delete操作
	shared_ptr<int> p6(new int(10), myDelete); // 指定删除器。 也可以三个参数: shared_ptr<int>(new int(10), myDelete, 内存分配器<>);  allocator<>
	p6.reset();
	p6.reset(new int(10), myDelete); // reset时同样可以指定删除器
	p6.reset();
	p6.reset(new int[10], [](int *p) { // 指向动态数组
		cout << "lambda 删除器" << endl;
		delete[] p; // 使用 delete[] 删除
	}); // lambda表达式
	p6.reset();
	// std::default_delete 标准库中的模板类,也可以作为自定义删除器
	shared_ptr<A> pA(new A[2], std::default_delete<A[]>()); // 删除类数组,注意写法
	pA.reset();
	pA.reset(new A[2], std::default_delete<A[]>());
	pA.reset();
	shared_ptr<A[]> pArr(new A[2]); // 删除类数组,也可以这么写,在 <>中加上[]
//	shared_ptr<A[]> pArr2 = make_shared<A[2]>(); // 不可以,指向数组不可以用make_shared<>函数
	pArr[0].num = 1; // 同样使用 [] 运算符获取数组元素 ,pArr[0] 获取的是元素值,所以成员运算符用 . 而不是 ->
	pArr[2].num = 2;
	pArr.reset();
	// 如果shared_ptr执行的对象是同种数据类型,即使删除器不同,这两个shared_ptr也是同一种数据类型的对象
	Vector<shared_ptr<A>> vec;
	vec.push_back(pA); // 可以
//	vec.push_back(pArr); // 不可以,这里虽然指向的也是A数组,但是其数据类型定义时是 shared_ptr<A[]> ,与vector的还是不一样的
	// make_shared函数创建的shared_ptr不能指定删除器。

	return 0;
}

5_4_weak_ptr.cpp

#include "hjcommon.hpp"
#include <memory>

using namespace std;
HJ_NS_USING

int main_2_5_4(int argc, char *argv[])
{
	int *p = new int(10); // 如果手动 delete p; 删除裸指针,shared_ptr是无法知道的。 所以建议不能这么操作。
	shared_ptr<int> ps1(p);// = make_shared<int>(10);
	weak_ptr<int> pw1(ps1); // 可以
	weak_ptr<int> pw2 = ps1; // 可以
	weak_ptr<int> pw3; // 默认也是nullptr
	pw3 = ps1; // 可以
	cout << ps1.use_count() << ", " << pw1.use_count() << endl; // 1, 1

	// lock() 新建一个对象的shared_ptr返回,强引用计数会+1,若该对象已释放,那么返回空
	shared_ptr<int> ps2 = pw1.lock();
	if (ps2) cout << "ps2 not nullptr. : " << ps2.use_count() << endl;
	else cout << "ps2 nullptr." << endl;
	// expired() 判断弱引用指向的对象是否已经被释放
	if (pw1.expired()) cout << "已过期." << endl; // if(pw1)会语法报错, weak_ptr的对象不能像shared_ptr对象一样作为条件表达式。
	else cout << "未过期." << endl;
	// weak_ptr.reset() 将弱引用计数-1,对强引用无影响。 无带参的reset函数
	pw1.reset();

	// 尺寸问题: shared_ptr 与 weak_ptr 的尺寸都是裸指针sizeof的两倍
	cout << sizeof p << "," << sizeof ps1 << "," << sizeof pw1 << endl; // 8,16,16

	return 0;
}

5_5_shared_ptr使用场景.cpp

#include "hjcommon.hpp"
#include <memory>

using namespace std;
HJ_NS_USING

class B;
class A : public std::enable_shared_from_this<A>
{
public:
	shared_ptr<A> getSelf()
	{
		// 安全返回this的智能指针对象(工作原理是weak_ptr.lock())。不要写成 shared_ptr<A>(this); 这样写等于用同一个裸指针对象初始化两个shared_ptr
		return shared_from_this();
	}

	shared_ptr<B> m_pb;
	~A() { cout << "~A." << endl; }
};
class B
{
public:
	weak_ptr<A> m_pa; // shared_ptr<A> m_pa;
	~B() { cout << "~B." << endl; }

	operator int() const { return 1; } // 定义类型转换运算符,使B的对象可以与NULL比较,可以作条件表达式,此优先级大于 nullptr 的。
	operator const std::nullptr_t() const { return nullptr; } // 定义类型转换运算符,可以作条件表达式,若想B的对象能与nullptr作比较,那么需要重载 ==、!= 运算符
	int operator==(const std::nullptr_t) { return 1; }
	int operator!=(const std::nullptr_t) { return 0; }
};

int main_2_5_5(int argc, char *argv[])
{
	// enable_shared_from_this<> 类模板
	shared_ptr<A> pa(new A);
	pa->getSelf();
	cout << pa.use_count() << endl; // 1
	// 交叉引用对象
	shared_ptr<B> pb(new B);
	pa->m_pb = pb; // 若交叉强引用对象,最后无法析构,造成内存泄漏。 将其中一个强引用换成弱引用即可解决此问题,因为析构pa与pb只会将引用计数-1,而交叉强引用计数是2
	pb->m_pa = pa;
	cout << pa.use_count() << ", " << pb.use_count() << endl; // 1, 2 ,注意A与B析构函数执行先后顺序:弱引用在谁,谁就后析构

	// 移动语义
	shared_ptr<int> ps1 = make_shared<int>(10);
	shared_ptr<int> ps2(std::move(ps1)); // 可以
	cout << ps2.use_count() << ", " << (ps1==nullptr) << endl; // 1, 1
	shared_ptr<int> ps3 = std::move(ps2); // 可以
	cout << ps3.use_count() << ", " << (ps2==nullptr) << endl; // 1, 1
	shared_ptr<int> ps4;
	ps4 = std::move(ps3); // 可以
	cout << ps4.use_count() << ", " << (ps3==nullptr) << endl; // 1, 1

	// 对象作条件表达式,与NULL、nullptr作比较
	B b;
	if (b) cout << "true." << endl; // true.
	else cout << "false." << endl;
	cout << (b==NULL) << ", " << (b==nullptr) << ", " << (b!=nullptr) << endl; // 1, 1, 0

	// 谨慎使用: new shared_ptr<T>; memcpy(shared_ptr); 奇怪用法先想清楚再用

	return 0;
}

5_6_unique_ptr.cpp

#include "hjcommon.hpp"
#include <memory>

using namespace std;
HJ_NS_USING

class A
{
public:
	~A() { cout << "~A." << endl; }
};

static shared_ptr<String> fromUnique() // 可以
{
	return unique_ptr<String>(new String("ly"));
}

static void uniqueDelete(String *str) // 自定义unique_ptr删除器
{
	cout << "删除函数 : " << *str << endl;
	delete str;
}

int main(int argc, char *argv[])
{
	unique_ptr<String> pu1(new String("some")); // 可以
//	unique_ptr<String> pu2(pu1); // 不可以,因为是独占式的,所以不支持拷贝构造与赋值函数
//	unique_ptr<String> pu3 = pu1; // 不可以
//	unique_ptr<String> pu4;
//	pu4 = pu1; // 不可以
	// c++14中才有 make_unique<T>() 函数
	unique_ptr<String> pu5 = make_unique<String>("some"); // 可以

	// 移动语义
	unique_ptr<String> pu6(std::move(pu5)); // 可以
	unique_ptr<String> pu7 = std::move(pu6); // 可以
	unique_ptr<String> pu8;
	pu8 = std::move(pu7); // 可以

	// release() 表示切断unique_ptr(会置空)与对象的联系,返回指向对象的裸指针,这个裸指针可以手动delete,也可赋值给其他智能指针
	unique_ptr<String> pu9(pu8.release());
	cout << (pu8==nullptr) << ", " << pu9->c_str() << endl;

	// reset() 与 shared_ptr.reset() 功能一样
	unique_ptr<String> pu10 = make_unique<String>("hj");
	pu10.reset(pu10.release());
	pu10 = nullptr; // 作用与reset()函数一样

	// 数组
	unique_ptr<A[]> pArr(new A[2]()); // 可以,unique_ptr<> 数组的使用与 shared_ptr<> 一样
//	unique_ptr<A[]> pArr2 = make_unique<A[3]>(); // 不可以

	// get() 与 shared_ptr<> 一样
	unique_ptr<String> pu11(new String("hj"));
	String *pstr = pu11.get();
	*pstr = "tyj";
	cout << *pu11 << endl; // tyj, 对于数组的智能指针是没有 * 运算符的,需要用 [] 运算符
	// swap()函数 与 shared_ptr<> 一样

	// 转换成shared_ptr<> : shared_ptr<> 不提供拷贝自unique_ptr<>左值 的函数, 但提供unique_ptr<>右值移动过来的。。
//	shared_ptr<String> ps1(pu11); // 不可以
//	shared_ptr<String> ps2 = pu11; // 不可以
//	shared_ptr<String> ps3;
//	ps3 = pu11; // 不可以
	shared_ptr<String> ps4(unique_ptr<String>(new String("zqt"))); // 可以
	shared_ptr<String> ps5 = unique_ptr<String>(new String("zqt")); // 可以
	shared_ptr<String> ps6;
	ps6 = unique_ptr<String, void(*)(String*)>(new String("zqt"), [](String *str) { // lambda表达式,unique_ptr删除器
		cout << "删除 : " << *str << endl;
		delete str;
	}); // 可以
	ps6 = fromUnique(); // 可以

	// unique_ptr删除器 : unique_ptr<T, 删除器类型> pu(new T, 删除器可调用对象);
	// decltype(obj) 表示获取可调用对象obj的数据类型, decltype(uniqueDelete) 获取的是 void(String*), decltype(uniqueDelete)* 表示 void(*)(String*)
	// 可以在名字后接()然后运行的对象就叫可调用对象。 包含:函数名、重载了operator()运算符的类的对象
	using myDel = decltype(uniqueDelete)*; // 等价于 typedef decltype(uniqueDelete)* myDel;
	unique_ptr<String, myDel> pu12(new String("lcx"), uniqueDelete); // 可以
	auto myLambda = [](String *str) { // lambda 删除器,lambda表达式可理解为带有重载了operator()运算符的类的对象,所以直接 decltype(myLambda) ,加 * 会报错。
		cout << "myLambda 删除器 : " << *str << endl;
		delete str;
	};
	unique_ptr<String, decltype(myLambda)> pu13(new String("ql"), myLambda); // 可以
	unique_ptr<String> pu14(new String("hmh"), std::default_delete<String>()); // 可以, default_delete 是 unique_ptr 默认删除器
	// 由于unique_ptr删除器是类模板参数,所以数据类型相同但删除器不同的unique_ptr是不同的数据类型,这点与shared_ptr不一样(shared_ptr删除器只是函数形参)

	// unique_ptr尺寸问题:与裸指针大小一样(未增加删除器的时候,增加了删除器尺寸可能会变化。 增加字节尺寸对效率有影响,需慎用)

	// auto_ptr
	auto_ptr<String> pa1(new String("ly"));
	auto_ptr<String> pa2 = pa1; // 语法不报错,但是这时候 pa1 会被置空,没有unique_ptr的语法报错,如果后续不小心再次使用 pa1 可能会造成程序异常。

	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值