記事ディレクトリ
実装プロセス
1.ポインタカウントクラス
class PtrCounter
{
public:
PtrCounter(): count{
1} {
}
void add(){
++count;}
void subtract(){
--count;}
int get() const{
return count;}
private:
std::atomic<int> count;
};
STLが提供する共有ポインターは、参照カウントの原則に基づいているため、このようなポインター参照カウントクラスも最初に定義します。このクラスには、3つのメンバー関数が含まれています。1つは、count + 1つのadd()関数、countから1つを引いたsubtract()関数、およびcount value get()関数です。アトミック型でカプセル化されたプライベート整数メンバー変数もあり、その初期値は1であり、そのアクセスによってデータ競合が発生しないことが保証され、異なるスレッド間でメモリアクセスを同期するために使用できます。
2.共有ポインタテンプレートクラス
template<typename T>
class SharePtr
{
public:
SharePtr(T* ptr) : my_ptr{
ptr}, my_ref_count{
new PtrCounter} {
}
SharePtr() : my_ptr(nullptr),my_ref_count(new PtrCounter){
}
SharePtr(const SharePtr &p)
{
this->my_ptr = p.my_ptr;
this->my_ref_count = p.my_ref_count;
my_ref_count->add();
}
SharePtr& operator = (const SharePtr &p)
{
clear();
this->my_ptr = p.my_ptr;
this->my_ref_count = p.my_ref_count;
my_ref_count->add();
return *this;
}
SharePtr(SharePtr &&p)
{
this->my_ptr = p.my_ptr;
this->my_ref_count = p.my_ref_count;
p.my_ptr = nullptr;
p.my_ref_count = nullptr;
}
SharePtr &operator =(SharePtr &&p)
{
clear();
this->my_ptr = p.my_ptr;
this->my_ref_count = p.my_ref_count;
p.my_ptr = nullptr;
p.my_ref_count = nullptr;
return *this;
}
~SharePtr(){
clear();}
int use_count() {
return my_ref_count->get();}
T* get() const {
return my_ptr;}
T* operator->() const {
return *my_ptr;}
T& operator*() const {
return *my_ptr;}
operator bool() const {
return my_ptr;}
private:
T* my_ptr;
PtrCounter* my_ref_count;
void clear()
{
if(my_ref_count)
{
my_ref_count->subtract();
if(my_ref_count->get() == 0)
{
if(my_ptr) delete my_ptr;
delete my_ref_count;
}
}
}
};
次は共有ポインタコアクラスです。このクラスには、2つのプライベートメンバーポインター変数があります。これらは、ポインター参照カウントタイプのポインターとテンプレートパラメータータイプのポインターです。クラスは2つのコンストラクターをオーバーロードします。1つはテンプレートパラメータータイプのポインターパラメーターを使用し、もう1つはパラメーターを使用せず、メンバー変数はコンストラクターで初期化されます。コピー構築は、シャローコピーを実行し、参照カウントを1つインクリメントします。代入関数は、シャローコピーを実行する前に重要なクリア操作を実行します。この操作は、最初にポインター参照カウントを1つデクリメントし、ポインター参照カウントが0かどうかを判別します。 。その場合は、2つのポインタメンバー変数を削除します。さらに、ムーブコピーコンストラクターとムーブ代入関数が定義されています。これら2つの関数と、最初の2つのコンストラクターとムーブ代入関数の違いは、テンプレートパラメータータイプのポインターメンバー変数をnullに設定するかどうかです。最後に、クリーンアップ操作を行うデストラクタがあります。
テスト
struct A
{
A() {std::cout<<"A()\n";}
~A(){std::cout<<"~A()\n";}
};
void test_sharePtr()
{
A* a = new A;
SharePtr<A> ptr(a);
{
std::cout<<ptr.use_count()<<std::endl;
//调用拷贝构造函数
SharePtr<A> b= ptr;
std::cout<<b.use_count()<<std::endl;
std::cout<<ptr.use_count()<<std::endl;
SharePtr<A> c;
//调用赋值函数
c = ptr;
std::cout<<c.use_count()<<std::endl;
std::cout<<ptr.use_count()<<std::endl;
}
std::cout<<ptr.use_count()<<std::endl;
}
試験結果: