C++11新特性 智能指针

news2024/11/16 11:34:20

智能指针

  • nuique_ptr
    • 特点
    • 不允许拷贝构造和赋值
    • 运算符重载-> () *
    • unique_ptr 删除器仿写
      • 删除文件
      • 删除普通对象
  • shared_ptr
    • 特点
    • 示意图
    • 仿写shared_ptr
      • 删除器部分特化
      • 拷贝构造 移动构造 && 左值赋值 和移动赋值
      • 完整实现
  • weak_ptr
    • 特点
    • weak_ptr 实现
    • 解决循环引用
    • 弱指针一个应用

nuique_ptr

unique_ptr在要表达"专属所有权"的语义时使用,即unique_ptr指针永远′拥有"其指向的对象,所以unique_ptr是一个move-only类型,一个unique_ptr指针是无法被复制的,只能将"所有权"在两个unique_ptr指针之间转移,转移完成后原来的unique_ptr将被设为null;
智能指针是比原始指针更智能的类,解决悬空(dangling)指针或多次删除被指向对象,以及资源泄露问题,通常用来确保指针的寿命和其指向对象的寿命一致。智能指针虽然很智能,但容易被误用,智能也是有代价的。

特点

1 .基于排他所有权模式:两个指针不能指向同一个资源。这一块资源只能被一个指针指向
2.由于独占对象的拥有权,所以不提供拷贝构造函数和左值赋值函数重载。浅拷贝构造和浅赋值都是两个对象同时指向一个资源,
深拷贝,深赋值,同样的资源复制了一份,被两个unique对象指向,不行。
3.提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存指针是安全。
6.unique_ptr具有->和*运算符重载符,因此它可以像普通指针一样使用。


class PtrInt {
private:
	int* pval;
public:
	PtrInt(int x=0):pval(new int(x)){
		cout << "Create PtrInt" << this << endl;
	}
	~PtrInt() {
		delete pval;
		pval = nullptr;
		cout << "Destroy PtrInt" << this << endl;
	}
	PtrInt(const PtrInt& it) :pval(new int(10)) {
		if (it.pval != nullptr) {
			*pval = *it.pval;
		}
		cout  << this<< "Copy Create PtrInt" <<&it<< endl;
	}
	PtrInt& operator=(const PtrInt& it) {
		if (this != &it) {
			delete pval;
			if (it.pval != nullptr) {
				pval = new int(*it.pval);
			}
		}
		cout << this << "operator=()" << &it << endl;
		return *this;
	}
	PtrInt(PtrInt&& it) :pval(it.pval) {
		it.pval = nullptr;
		cout<<this << "Move Create PtrInt" << &it << endl;
	}
	PtrInt& operator=(PtrInt&& it) {
		if (this != &it) {
			delete[]pval;
			pval = it.pval;
			it.pval = nullptr;
		}
		cout << this << "Move operatro=()" << &it << endl;
		return *this;
	}
	void SetValue(int x) { *pval = x; }
	int GetValue()const { return *pval; }
	void Print()const {
		if (pval != nullptr) {
			cout << *pval << endl;
		}
	}

};


template<class _Ty>
class my_unique_ptr {
public:
	using pointer = _Ty*;
	using element_type = _Ty;
private:
	pointer mPtr;
public:
	my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}
	//唯一性指针没有拷贝构造和左值赋值
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;
	~my_unique_ptr() {
		if (mPtr != nullptr) {
			delete mPtr;
		}
		mPtr = nullptr;
	}
};

int main() {
	unique_ptr<PtrInt>pa(new PtrInt(10));
	pa->Print();
	return 0;
}

在这里插入图片描述
my_unique_ptrpa(new PtrInt(100));
在这里插入图片描述

不允许拷贝构造和赋值

唯一性智能指针不允许拷贝构造:浅拷贝pa pb的mPtr会指向同一个空间,析构时会把同一个地址释放两次,深拷贝就有了两份空间,应该只有一份

my_unique_ptr(const my_unique_ptr&) = delete;

也不允许赋值

my_unique_ptr& operator=(const my_unique_ptr&) = delete;

可以实现移动拷贝构造和移动赋值
移动之后,资源就不是以前的了,强行访问会崩溃

my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {
		x.mPtr = nullptr;
}
	//pa=move(pb);  //unique_
my_unique_ptr operator=(my_unique_ptr&& other) {
		if (this == &other)return *this;
		//my_unique_ptr(move(other)).swap(*this); 与下面三行等价
		delete mPtr;
		mPtr = other.mPtr;
		other.mPtr = nullptr;

		return *this;
}

运算符重载-> () *

在这里插入图片描述
pa是一个对象,访问对象的成员函数用.
为啥可以pa->
-> 都是用在结构体指针,->就是 先解引用,再 . 访问对象成员函数
这里重载了->
pa->Print();
pa.operator->()->Print();
等价

	pointer get()const { return mPtr; }
	_Ty& operator*()const { return *get(); }
	_Ty* operator->()const { return get(); }

在这里插入图片描述

int main() {
    unique_ptr<Int>pa(new Int(100));
    
	pa->Print();
	pa.operator->()->Print();
	
	return 0;
}
class Int {
	int value;
public:
	Int(int x = 0) :value(x) {
		cout << "Create Int " << endl;
	}
	~Int() { cout << "Destroy Int " << endl; }
	void SetValue(int x) { value = x; }
	int GetValue()const {
		return value;
	}
	void Print()const { cout << value << endl; }
};


template<class _Ty>
class my_unique_ptr {
public:
	using pointer = _Ty*;
	using element_type = _Ty;
private:
	pointer mPtr;
public:
	my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;
	my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {
		x.mPtr = nullptr;
	}
	
	~my_unique_ptr() {
		if (mPtr != nullptr) {
			delete mPtr;
		}
		mPtr = nullptr;
	}
	pointer get()const { return mPtr; }
	_Ty& operator*()const { return *get(); }
	_Ty* operator->()const { return get(); }


};

int main() {
    unique_ptr<Int>pa(new Int(100));
	pa->Print();
	pa.operator->()->Print();

	return 0;
}

在这里插入图片描述
重载返回的mPtr ,*pa直接就是int对象,所以可以直接修改

int main() {
    unique_ptr<int>pa(new int(10));
	*pa = 100;
	pa.operator*() = 100;


	my_unique_ptr<Int>pb(new Int(10));
	//*pb返回的是一个Int对象 对象用. 访问成员函数Print()
	(*pb).Print();
	//上面已经解释过了,重载->
	pb->Print();

	return 0;
}

在这里插入图片描述

template<class _Ty>
class my_unique_ptr {
public:
	using pointer = _Ty*;
	using element_type = _Ty;
private:
	pointer mPtr;
public:
	my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;
	my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {
		x.mPtr = nullptr;
	}
	//pa=move(pb);  //unique_
	my_unique_ptr operator=(my_unique_ptr&& other) {
		if (this == &other)return *this;
		//my_unique_ptr(move(other)).swap(*this); 与下面三行等价
		delete mPtr;
		mPtr = other.mPtr;
		other.mPtr = nullptr;

		return *this;
	}

	
	~my_unique_ptr() {
		if (mPtr != nullptr) {
			delete mPtr;
		}
		mPtr = nullptr;
	}
	pointer get()const { return mPtr; }
	_Ty& operator*()const { return *get(); }
	_Ty* operator->()const { return get(); }
	pointer release() {
		pointer old = mPtr;
		mPtr = nullptr;
		return old;
	}
	void reset(pointer ptr = nullptr) {
		if (mPtr != nullptr) {
			delete mPtr;
		}
		mPtr = ptr; 
	}
	void swap(my_unique_ptr& ptr) {
		swap(this->mPtr, it.mPtr);
	}
};

unique_ptr 删除器仿写

对申请的系统资源,重写一个删除器类型,专门针对系统资源释放,如果是new
则需要delete 如果是文件fopen,则需要用fclose

删除文件

template<class _Ty,class _Dx=my_default_deleter<_Ty>>
class my_unique_ptr {
public:
	using pointer = _Ty*;
	using element_type = _Ty;
	using deleter_type = _Dx;
private:
	pointer mPtr;
	deleter_type deleter;
public:
	deleter_type get_deleter()const { return deleter; }
	my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;
	my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {
		x.mPtr = nullptr;
	}
	//pa=move(pb);  //unique_
	my_unique_ptr operator=(my_unique_ptr&& other) {
		if (this == &other)return *this;
		//my_unique_ptr(move(other)).swap(*this); 与下面三行等价
		delete mPtr;
		mPtr = other.mPtr;
		other.mPtr = nullptr;

		return *this;
	}

	
	~my_unique_ptr() {
		if (mPtr != nullptr) {
			get_deleter() (mPtr);
		}
		mPtr = nullptr;
	}
	pointer get()const { return mPtr; }
	_Ty& operator*()const { return *get(); }
	_Ty* operator->()const { return get(); }
	pointer release() {
		pointer old = mPtr;
		mPtr = nullptr;
		return old;
	}
	void reset(pointer ptr = nullptr) {
		if (mPtr != nullptr) {
			get_deleter()(mPtr);
		}
		mPtr = ptr; 
	}
	void swap(my_unique_ptr& ptr) {
		swap(this->mPtr,ptr.mPtr);
	}
};
struct FileDeleter {
	void operator()(FILE*ptr)const  {
		fclose(ptr);
	}
};
int main() {
my_unique_ptr<FILE>pa(fopen("xst.txt","w"));
	fprintf(pa.get(), "yhping\n");
	return 0;
}

删除普通对象

一组对象和单个对象的delete

template<class _Ty>
struct my_default_deleter {
	void operator()(_Ty* ptr)const {
		delete ptr;
	}
};
template<class _Ty>
struct my_default_deleter<_Ty[]> {
	void operator()(_Ty* ptr)const {
		delete[] ptr;
	}
};


template<class _Ty, class _Dx = my_default_deleter<_Ty>>
class my_unique_ptr{
public:
	using pointer = _Ty*;
	using element_type = _Ty;
	using deleter_type = _Dx;
private:
	pointer mPtr;
	deleter_type deleter;
public:
	deleter_type get_deleter()const { return deleter; }
	my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;
	my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {
		x.mPtr = nullptr;
	}
	//pa=move(pb);  //unique_
	my_unique_ptr operator=(my_unique_ptr&& other) {
		if (this == &other)return *this;
		//my_unique_ptr(move(other)).swap(*this); 与下面三行等价
		delete mPtr;
		mPtr = other.mPtr;
		other.mPtr = nullptr;

		return *this;
	}

	
	~my_unique_ptr() {
		if (mPtr != nullptr) {
			get_deleter() (mPtr);
		}
		mPtr = nullptr;
	}
	pointer get()const { return mPtr; }
	_Ty& operator*()const { return *get(); }
	_Ty* operator->()const { return get(); }

	pointer release() {
		pointer old = mPtr;
		mPtr = nullptr;
		return old;
	}
	void reset(pointer ptr = nullptr) {
		if (mPtr != nullptr) {
			get_deleter()(mPtr);
		}
		mPtr = ptr; 
	}
	void swap(my_unique_ptr& ptr) {
		swap(this->mPtr,ptr.mPtr);
	}
};

class Int {
	int value;
public:
	Int(int x = 0) :value(x) {
		cout << "Create Int " << endl;
	}
	~Int() { cout << "Destroy Int " << endl; }
	void SetValue(int x) { value = x; }
	int GetValue()const {
		return value;
	}
	void Print()const { cout << value << endl; }
};


int main() {
	my_unique_ptr<Int[]>pa(new Int[10]{1,2,3,4,5,6,7,8,9,10});
	for (int i = 0; i < 10; i++) {
		pa[i].Print();
	}
	return 0;
}

在这里插入图片描述

int main() {
std::unique_ptr<Int> pa(new Int(10));
std::unique_ptr<Int> pb = std::make_unique<Int>(20);
std::unique_ptr<Int[]> pca(new Int[10]{ 1,2,3,4,5,6,7,8,9,10 }); 
std::unique_ptr<Int[]> pcb = std::make_unique<Int[]>(10);//不能初始化
return 0;
}

shared_ptr

shared_ptr实现了共享所有权(shared ownership)方式来管理资源对象,这意味没有一个特定的std::shared_ptr拥有资源对象。相反,这些指向同一个资源对象的stdushared_ptr相互协作来确保该资源对象在不需要的时候被析构。

特点

1 .基于共享所有权模式:多个指针能够同时指向同一个资源。
2.基于共享所有权,使用引用计数控制块管理资源对象的生命期。
3.提供拷贝构造函数和赋值重载函数;提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存shared_ptr对象是安全。
6 .shared_ptr重载了operator->和operator*运算符,因此它可以像普通指针一样使用。
两个共享指针指向同一个资源,该资源的引用计数加一

示意图

和唯一性指针的区别是加了一个引用计数块,一个资源一个引用计数块
在这里插入图片描述

int main() {
	shared_ptr<int>pa(new int(10));
	cout << pa.use_count() << endl;
	shared_ptr<int>pb(pa);
	cout << pb.use_count();
	return 0;
}

在这里插入图片描述

atomic<int>_Uses;

原子操作,使其过程不能被打断。

	~my_shared_ptr() {
		if (mRep != nullptr && mRep->Decref() == 0) {
			mDeleter(mPtr);
			delete mRep;
		}

资源需要用删除器删除,因为资源可能是API构建,释放也要API释放,而引用计数是new 所以释放 就是 delete
在这里插入图片描述

仿写shared_ptr

删除器部分特化

template<class _Ty>
struct My_Deleter
{
public:
	void operator()(_Ty* p) const
	{
		delete p;
	}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:
	void operator()(_Ty* p) const
	{
		delete []p;
	}
};

拷贝构造 移动构造 && 左值赋值 和移动赋值

拷贝构造实现的时候,两个shared_ptr指向的是同一个资源,同一个引用技术块,引用的uses+1

My_shared_ptr(const My_shared_ptr& x)
		:mPtr(x.mPtr), mRep(x.mRep)
	{
		if (mRep != nullptr)
		{
			mRep->Incref();
		}
	}
	My_shared_ptr(My_shared_ptr&& other)
		:mPtr(other.mPtr),
		 mRep(other.mRep)
	{
		mPtr = nullptr;
		mRep = nullptr;
	}

分析一下这个程序:左值赋值
My_shared_ptr(x) 类型名加括号,构建不具名对象,调用拷贝构造,引用计数加1,是个将亡值,然后析构将亡值,引用计数减1,发现uses为0 ,就删除了 pa指向的资源和引用计数块,
如果pa指向的资源uses不是1,那么就不会删除该资源和引用计数块
在这里插入图片描述
移动赋值
如果原来的资源的引用计数为1,移动后需要释放,不为1 就减1
移动赋值后,指针pb就跟原来的资源么有多了关系,pb->printf 会崩,而左值赋值,有关系
std::move(other) 转成右值,

	My_shared_ptr& operator=(const My_shared_ptr& x)
	{
		if (this == &x) return *this;
		My_shared_ptr(x).swap(*this);
		return *this;
	}
	My_shared_ptr& operator=(My_shared_ptr&& other)
	{
		if (this == &other) return *this;
		My_shared_ptr(std::move(other)).swap(*this);
		return *this;
	}

完整实现

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include<atomic>
using namespace std;
class Int
{
private:
	int value;
public:
	Int(int x = 0) :value(x) { cout << "Create Int: " << endl; }
	Int(int x, int y) :value(x + y) { cout << "Create Int(int,int)" << endl; }
	~Int() { cout << "Destroy Int " <<value<<endl; }
	Int(const Int& it) :value(it.value)
	{
		cout << "copy Create Int: " << this << endl;
	}
	Int& operator=(const Int& it)
	{
		if (this != &it)
		{
			value = it.value;
		}
		cout << "operator=()" << endl;
		return *this;
	}
	void PrintInt() const
	{
		cout << value << endl;
	}
	int& Value() { return value; }
	const int& Value() const { return value; }

	operator int() const { return value; }
};


template<class _Ty>
struct My_Deleter
{
public:
	void operator()(_Ty* p) const
	{
		delete p;
	}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:
	void operator()(_Ty* p) const
	{
		delete []p;
	}
};


template<class _Ty>
class My_RefCount
{
public:
	using element_type = _Ty;
	using pointer = _Ty*;
private:
	_Ty* _Ptr;
	std::atomic<int> _Uses; // shared;
	std::atomic<int> _Weaks; // weak_ptr;
public:
	My_RefCount(_Ty* ptr = nullptr) :_Ptr(ptr), _Uses(0), _Weaks(0)
	{
		if (_Ptr != nullptr)
		{
			_Uses = 1;
			_Weaks = 1;
		}
	}
	~My_RefCount() = default;
	void Incref() { ++_Uses; }
	void Incwref() { ++_Weaks; }
	int Decref()
	{
		if (--_Uses == 0)
		{
			Decwref();
		}
		return _Uses;
	}
	int Decwref()
	{
		return --_Weaks;
	}
	int _use_count() const
	{
		return _Uses.load();
	}
};



template<class _Ty,class _Dx = My_Deleter<_Ty>>
class My_shared_ptr
{
public:
	using MyDeleter = _Dx;
private:
	_Ty*               mPtr;
	My_RefCount<_Ty> * mRep;
	MyDeleter          mDeleter;
public:
	My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr)
	{
		if (mPtr != nullptr)
		{
			mRep = new My_RefCount<_Ty>(mPtr);
		}
	}
	My_shared_ptr(const My_shared_ptr& x)
		:mPtr(x.mPtr), mRep(x.mRep)
	{
		if (mRep != nullptr)
		{
			mRep->Incref();
		}
	}
	My_shared_ptr(My_shared_ptr&& other)
		:mPtr(other.mPtr),
		 mRep(other.mRep)
	{
		mPtr = nullptr;
		mRep = nullptr;
	}
	My_shared_ptr& operator=(const My_shared_ptr& x)
	{
		if (this == &x) return *this;
		My_shared_ptr(x).swap(*this);
		return *this;
	}
	My_shared_ptr& operator=(My_shared_ptr&& other)
	{
		if (this == &other) return *this;
		My_shared_ptr(std::move(other)).swap(*this);
		return *this;
	}

	~My_shared_ptr()
	{
		if (nullptr != mRep && 0 == mRep->Decref())
		{
			mDeleter(mPtr);
			delete mRep;
		}
		mPtr = nullptr;
		mRep = nullptr;
	}

	int use_count() const
	{
		return mRep != nullptr ? mRep->_use_count() : 0;
	}

	_Ty * get() const { return mPtr; }
	_Ty& operator*() const { return *get(); }
	_Ty* operator->() const {return get();}
	operator bool() const { return mPtr != nullptr; }
	void swap(My_shared_ptr& other)
	{
		std::swap(this->mPtr, other.mPtr);
		std::swap(this->mRep, other.mRep);
	}
	void reset()
	{
		if (mRep != nullptr && 0 == mRep->Decref())
		{
			mDeleter(mPtr);
			delete mRep;
		}
		mRep = nullptr;
		mPtr = nullptr;
	}
	void reset(_Ty* p)
	{
		if (nullptr == p)
		{
			reset();
			return;
		}
		if (mRep != nullptr && 0 == mRep->Decref())
		{
			mDeleter(mPtr);
			delete mRep;
			mRep = nullptr;
		}
		mPtr = p;
		if (mPtr != nullptr)
		{
			mRep = new My_RefCount(mPtr);
		}
	}
};

weak_ptr

特点

弱引用指针weak_ptr是用来监视shared_ptr的生命周期,是shared_ptr的一个助手。weak_ptr没有重载操作符*和->,因为它不与shared_ptr共享指针,不能操作资源,主要是通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中管理的资源是否存在。weak_ptr还可以用来返回this 指针和解决循环引用的问题。

当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。可以通过use_count()方法来获得当前观测资源的引用计数。

weak_ptr 实现

template<class _Ty>
class My_weak_ptr
{
private:
	My_RefCount<_Ty>* mRep;
public:
	My_weak_ptr() :mRep(nullptr) {}
	My_weak_ptr(const My_shared_ptr<_Ty>& other)
	{
		mRep = other.mRep;
		if (mRep != nullptr)
		{
			mRep->Incwref();
		}
	}
	~My_weak_ptr()
	{
		if (mRep != nullptr)
		{
			mRep->Decwref();
		}
	}
};

解决循环引用

class Parent;
class Child
{
public:
	//std::shared_ptr<Parent> parent;
	std::weak_ptr<Parent> parent;
	Child() { cout << "Create Child" << endl; }
	~Child() { cout << "~Child" << endl; }
};
class Parent
{
public:
	//std::shared_ptr<Child> child;
	std::weak_ptr<Child> child;
	Parent() { cout << "Create Parent" << endl; }
	~Parent() { cout << "~Parent" << endl; }
	void Hi() { cout << "hello yhping" << endl; }
};
int main()
{
	std::shared_ptr<Parent> pa(new Parent());
	std::shared_ptr<Child>  cp(new Child());
	pa->child = cp;
	cp->parent = pa;
	return 0;
}


在这里插入图片描述
无法释放对象,因为use减为了1,调用析构函数时,无法删除 pa指向的资源和计数块
用弱指针就能解决该问题

弱引用指针只能对weaks加1,共享性指针只能对uses加一
在这里插入图片描述
只有当uses和weaks同时为0,才能删除该资源
uses为0可以析构资源,但是不能析构计数块,weaks为0 uses为0 才能析构计数块
过程就是:uses减1 weaks减1 释放child对象 ,该对象有mRep 所以 parent里面的weaks减1,此时析构child对象,此时析构pa,uses减1,为0,此时weaks为0,可以将该计数块析构,所以将parent对象释放,有弱引用指针,此时将最右边的计算计数块,weaks为0,
析构计数块,析构parent对象在这里插入图片描述

弱指针一个应用

应用就是
删除父节点,子节点也会被自动释放删除,析构子节点,父节点不会被析构

template<class KeyType>
class BSTree
{
public:
	struct BstNode
	{
		std::shared_ptr<BstNode> leftchild;
		std::weak_ptr<BstNode> parent;
		std::shared_ptr<BstNode> rightchild;
		KeyType key;
	public:
		BstNode(const KeyType& kx)
			:key(kx)
		{
		}
		~BstNode()
		{

		}
	};
private:
	 std::shared_ptr<BstNode> root;

	static void InOrder(std::shared_ptr<BstNode> ptr)
	{
		if (ptr)
		{
			InOrder(ptr->leftchild);
			cout << ptr->key << " ";
			InOrder(ptr->rightchild);
		}
	}
public:
	BSTree() :root(nullptr) {}
	~BSTree()
	{

	}
	bool Insert(const KeyType& kx)
	{
		if (!root)  // root.operator bool()
		{
			root = std::make_shared<BstNode>(kx);  // Root;
			return true;
		}
		std::shared_ptr<BstNode> pa(nullptr);
		std::shared_ptr<BstNode> p = root;
		while (p && p->key != kx)
		{
			pa = p;
			p = kx > p->key ? p->rightchild : p->leftchild;
		}
		if (p && p->key == kx)
		{
			cout << "key 重复 " << endl;
			return false;
		}

		p = std::make_shared<BstNode>(kx);
		p->parent = pa;
		if (p->key > pa->key)
		{
			pa->rightchild = p;
		}
		else
		{
			pa->leftchild = p;
		}

		return true;
	}

	void InOrder() const
	{
		InOrder(root);
		cout << endl;
	}
};
int main()
{
	int ar[] = { 53,17,78,9,45,65,87,23,81,94,88 };
	BSTree<Int> mytree;
	Int tmp;
	for (auto& x : ar)
	{
		tmp.Value() = x;
		mytree.Insert(tmp);
	}
	mytree.InOrder();
	return 0;
}
shared_ptr<Int>pa(new Int(10));
shared_ptr<Int>pb=make_shared<Int>(20);

区别是pa 用了两次new 第二次开辟计数块的空间,而pb开辟一次,

weaks就是只要是指针指向,就算一个,弱指针和共享指针都可以,而uses必须是shared指针,才算数
pb空间只有等weaks为0才能释放,mPtr指向的和mPep指向的绑在一起了

My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr)
{
	if (mPtr != nullptr)
	{
		mRep = new My_RefCount<_Ty>(mPtr);
	}
}

创建共享性指针的时候,有两个参数 ,指定所需的删除器,唯一性指针不具有,弱指针不具备删除对象

shared_ptr<Int>pa(new Int(10),DeleteInt);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/707776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java: 警告: 源发行版 11 需要目标发行版 11解决方案

出现这样的问题首先检查一下自己的项目结构是否使用的对应的jdk 如果这里是正确的&#xff0c;之后查看一下自己的pom文件中是否指定了正确的jdk 这里的时候你改完运行就会发现还会报错&#xff0c;一定要记得刷新一下maven 再重新启动项目&#xff0c;即解决

剑指 Offer 63: 股票的最大利润

最标准答案 不可以有前一项的影响&#xff0c;只能用来对比并不叠加 这里max设置0就会导致先行进入大于max的判断语句&#xff01; 无语了&#xff0c;自己把问题想的太复杂了&#xff01; class Solution {public int maxProfit(int[] prices) {if(prices.length<2) retur…

十二个常用化学文献检索网站

一、Royal Society of Chemistry英国皇家化学学会 英国皇家化学学会&#xff08;Royal Society of Chemistry&#xff0c;简称RSC&#xff09;&#xff0c;是一个国际权威的学术机构&#xff0c;是化学信息的一个主要传播机构和出版商&#xff0c;其出版的期刊及资料库一向是化…

886. 可能的二分法

链接&#xff1a;886. 可能的二分法 题解&#xff1a; class Solution { public:bool possibleBipartition(int n, vector<vector<int>>& dislikes) {// -1&#xff0c;代表这个点没有访问过&#xff0c; 0&#xff0c;1代表两个染色的组std::vector<int&…

python机器学习——聚类评估方法 K-Means聚类 神经网络模型基础

目录 聚类模型的评价方法&#xff08;1&#xff09;轮廓系数&#xff1a;&#xff08;2&#xff09;评价分类模型 【聚类】K-Means聚类模型&#xff08;1&#xff09;聚类步骤&#xff1a;&#xff08;2&#xff09;sklearn参数解析&#xff08;3&#xff09;k-means算法特点 神…

GPT模型训练实践(3)-参数训练和代码实践

一、参数训练 GPT模型参数的训练过程宏观上有两个大环节&#xff0c;先从上往下进行推理&#xff0c;再从下往上进行训练&#xff0c;具体过程为&#xff1a; 1、模型初始化参数随机取得&#xff1b; 2、计算模型输出与真实数据的差距&#xff08;损失值和梯度&#xff09; …

VS2019的安装和简单使用

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

【数据结构与算法】学校运动会管理系统(C/C++)

这是一个完整的项目&#xff0c;若有需要整个项目的压缩包&#xff08;源代码、文档、md文件等&#xff09;可私聊发送"学校运动会管理系统"。 问题描述 在“学校运动会管理系统”中&#xff0c;设有n个单位参加运动会&#xff08;单位可是学院、系、年级等&#xf…

Java 实现支付宝支付、退款、订单查询

最在开发一款APP&#xff0c;需要实现支付宝支付&#xff0c;记录一下实现过程 流程整体交互图如下所示 一、引入pom依赖 <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.0.3<…

【Java可执行命令】(八)JWS应用程序启动工具 javaws:深入解析Java Web Start应用程序的启动工具javaws ~

Java可执行命令之javaws 1️⃣ 概念&#x1f50d;JNLP (Java Network Launch Protocol) &#xff1f; 2️⃣ 优势3️⃣ 使用3.1 语法3.1.1 运行选项&#xff1a;-Xnosplash3.1.2 运行选项&#xff1a;-wait3.1.3 控制选项&#xff1a;-import [导入选项] < jnlp-file> 4️…

IDEA中删除某个模块后在创建同名模块显示已存在 的解决方案

IDEA中删除某个模块后在创建同名模块显示已存在 的解决方案 出现的问题复现解决方案成功添加后可能会出现的问题 出现的问题复现 前提是你认为已经删干净了&#xff0c;因为删除模块得删除两次才能从本地的文件夹中删除。 解决方案 右击项目名 找到 这个玩意儿&#xff0c;点…

MySQL不常用但非常实用的函数在项目中的应用

MySQL内置了不少函数&#xff0c;利用这些函数可以很好地在进行数据查询时候&#xff0c;进行数据处理&#xff0c;如果要查看MySQL所有的内置函数&#xff0c;可以在官网的文档中&#xff1a;Built-In Function and Operator Reference 有很详细的表格&#xff0c;列举了所有…

代码随想录二刷day39 |动态规划 之 62.不同路径 63. 不同路径

day39 62.不同路径确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组的初始化确定遍历顺序举例推导dp数组 63. 不同路径 II确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 62.不…

模仿QQ之右键菜单

参考&#xff1a;QT多级菜单 - 知乎 (zhihu.com) 运行效果图&#xff1a; 关键代码&#xff1a; void personMenu::contextMenuEvent(QContextMenuEvent *event) {//我完全可以写出一个代码生成器来把这些代码生成出来。parentnew QMenu(this);parent->addAction(QIcon(…

C++ 多态详解附图与代码

一、多态 1.1 什么是多态 多态是面向对象编程中的一个重要概念&#xff0c;它允许在不同类型的对象上执行相同的操作&#xff0c;并根据对象的实际类型来决定具体执行哪个操作。通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象…

2019年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&#xff0c;题目常常看&a…

【排序算法】堆排序

堆与一维数组 建立堆与一维数组的联系 堆排序并不是直接对堆节点Node类型排序&#xff0c;而是通过建立索引之间的关系&#xff0c;对一维数组排序。 称之为堆排序&#xff0c;是因为节点索引值之间的关系与完全二叉树的非常类似&#xff0c;而树又称堆。 设根节点为i&#xff…

【C#】委托、匿名方法、Lambda表达式和事件

【C#】委托、匿名方法、Lambda表达式和事件 委托 什么是委托&#xff1f; 委托和类一样&#xff0c;是用户自定义类型&#xff0c;是方法&#xff08;函数&#xff09;的抽象。通俗讲&#xff0c;委托就是 自定义类型的方法&#xff08;函数&#xff09;的代表。 声明委托 …

HTML+CSS+JavaScript华为主页

样式&#xff1a; HTMLCSSJavaScript仿华为首页 HTML: <!DOCTYPE html> <html><head><meta charset"utf-8"><link rel"stylesheet" type"text/css" href"Homepage.css"/><script type"text/ja…

NextJs下浅尝Prisma+Sqlite+逆向生成数据模型

1.安装prisma npm install prisma/client 2.创建schema.prisma npx prisma init 执行完命令后创建文件目录如下&#xff1a; 3.配置数据库连接 generator client {provider "prisma-client-js" }datasource db {provider "sqlite" //数据库类型 这…