スマートポインタのいくつかの一般的なポインタの例の分析

目次

追加の知識ポイント

一般的に使用される示唆的なマクロ定義者

スマートポインター

スマートポインターの基本原則

通常のポインタのメンバーアクセス機能をシミュレートする

通常のポインタの逆参照機能をシミュレートします

メモリの自動解放とアドレスのコピーをシミュレートします

スマートポインターの分類

ディープコピーとシャローコピー

ディープコピーの2つの用途

メモリの繰り返し解放を避けるためにコピーする

パラメータを転送するときの削除の問題を回避する

除去の問題の手順

手続き上の問題の説明

切除問題を解決するための手順

小さな変更のみ

値をパラメーターに直接コピーするよりも、ポインターを渡す方がよいのはなぜですか?

通常、親ポインタが関数パラメータとして使用されるのはなぜですか?

破壊的なコピー

破壊的コピースマートポインタの基本的な実装原則

破壊的なポインタを適用するときは注意してください

メモリライブラリの破壊ポインタ「auto_ptr」を呼び出して、上記と同じ操作を実行します

C ++ 11の新しいスマートポインター:メモリライブラリ内のunique_ptrスマートポインター

Unique_ptrスマートポインタアプリケーションの例

ただし、移動移動セマンティクス(構文)を使用して、unique_ptrのコンテンツを移動できます。


追加の知識ポイント

一般的に使用される示唆的なマクロ定義者

 

関数宣言の正式なパラメーターには、_In_および_out_修飾子があり、_In_および_out_はここではマクロです。

_に_

この変数またはパラメーターが入力値であることを示します。つまり、この変数を入力して、実行のために関数に送信する必要があります。

_でる_

これが出力値であることを示します。つまり、正式なパラメータにアドレスを渡すことができ、関数はこのアドレスに書き込みます

これらの2つのマクロはコンパイルに参加しません。これらはプログラマーへのリマインダーにすぎず、プログラマーに関数の呼び出し方法を理解させます。

スマートポインター

スマートポインターの基本原則

#include <iostream>  
#include <sstream>  
#include <string>  
using namespace std;  
  
struct Student   
{  
    string name;  
    float mark;  
    string Output;  
  
    Student(string name, float mark)  
    {  
        cout << "Student()的构造函数被调用" << endl;  
        this->name = name;  
        this->mark = mark;  
    }  
    ~Student()  
    {  
        cout << "Student()析构函数被调用" << endl;  
    }  
    explicit operator const char* () // 只能显式的调用类型转换符  
    {  
        ostringstream OutputString;  
        OutputString << this->name << "的成绩为" << this->mark;  
        Output = OutputString.str(); // ostrinstream返回一个string类型的变量  
        return Output.c_str(); // 将string类型的变量转换为char*类型的变量  
    }  
};  
  
template<typename T>  
struct Smart_Pointer  
{  
    T* ObjectPointer;  
    Smart_Pointer(T* Sptr)  
    {  
        cout << "智能指针的构造函数被调用" << endl;  
        ObjectPointer = Sptr;  
    }  
    ~Smart_Pointer()  
    {  
        cout << "智能指针的析构函数被调用" << endl;  
        delete ObjectPointer; // 当指针生命周期结束,指针所指向的对象自动销毁  
    }  
    T& operator = (T* Sptr)  
    {  
        this->ObjectPointer = Sptr;  
        return *this;  
    }  
    T& operator * () // 隐含参数为this指针  
    {  
        return *(this->ObjectPointer);  
    }  
    T* operator -> () // 隐含参数为this指针  
    {  
        return this->ObjectPointer;  
    }  
};  
  
int main()  
{  
    Student* stud1 = new Student{ "张三", 89.6 };  
    Smart_Pointer<Student> StudPtr(stud1);  
    cout << StudPtr->mark << endl; // 模拟普通指针访问成员  
    cout << (const char*)*StudPtr << endl; // 模拟普通指针进行解引用操作  
}  

 

スマートポインタはテンプレートクラスです。スマートポインタの利点は、「スマートポインタのライフサイクルが終了すると、ポインタが指すストレージスペースを解放できるため、メモリを自動的に管理するスマートポインタの単純な動作をシミュレートできる」ことです。

スマートポインタを真に「スマート」にするのは、コピーコンストラクタ、割り当て演算子、およびデストラクタの実装であることに注意してください。これらは、スマートポインタオブジェクトが関数に渡されるか、割り当てられるか、スコープ外にあるか(つまり、他のオブジェクトと同様)を決定します。同じことが破壊されます)。

通常のポインタのメンバーアクセス機能をシミュレートする

   T* operator -> () // 隐含参数为this指针  
    {  
        return this->ObjectPointer;  
    }  

 

通常のポインタの逆参照機能をシミュレートします

   T& operator * () // 隐含参数为this指针  
    {  
        return *(this->ObjectPointer);  
    }  

 

メモリの自動解放とアドレスのコピーをシミュレートします

    Smart_Pointer(T* Sptr)  
    {  
        cout << "智能指针的构造函数被调用" << endl;  
        ObjectPointer = Sptr;  
    }  
    ~Smart_Pointer()  
    {  
        cout << "智能指针的析构函数被调用" << endl;  
        delete ObjectPointer; // 当指针生命周期结束,指针所指向的对象自动销毁  
    }  
    T& operator = (T* Sptr)  
    {  
        this->ObjectPointer = Sptr;  
        return *this;  
    }  

 

スマートポインターの分類

スマートポインタの分類は、実際にはメモリリソース管理戦略の分類であり、次のカテゴリに分類できます。

ディープコピー

書き込み時コピー(COW)

参照数

参照リンク

破壊的なコピー

以下、「ディープコピー」と「破壊コピー」に焦点を当てます。

ディープコピーとシャローコピー

ディープコピー

率直に言って、自分にコピーするオブジェクトの内容をコピーすることを意味します。こうすることで、内容は同じであるが互いに独立した2人の個人が形成されます。

浅いコピー

率直に言って、他人のアドレスを自分にコピーすることです。これは一般的には問題ありませんが、コピーされたオブジェクトにnewから動的に要求されたメモリがあると、それは終了します。コピーされたオブジェクトと私のライフサイクルの終わりに、同じメモリ領域を両側で繰り返し解放する必要があり、プログラムが崩壊します

ディープコピーの2つの用途

繰り返しメモリが解放されないようにコンテンツをコピーする

関数パラメーターを渡すときに、サブクラスから親クラスへの「問題の切り取り」を回避する

メモリの繰り返し解放を避けるためにコピーする

#include <iostream>  
#include <string>  
using namespace std;  
  
struct Student   
{  
    char* name;  
    float mark;  
  
    Student(const char* name, float mark)  
    {  
        if (this->name != name)  
        {  
            cout << "调用Student拷贝构造函数" << endl;  
            this->name = new char[strlen(name) + 1]; // 动态申请空间  
            memset(this->name, strlen(name) + 1, 0); // 初始化字符数组中的每个元素  
            strcpy(this->name, name); // 字符数组的拷贝  
            this->mark = mark;  
        }  
    }  
    ~Student()  
    {  
        if (this->name != NULL)  
        {  
            cout << "调用Student析构函数" << endl;  
            delete[] this->name; // 内存的释放  
        }  
    }  
    friend ostream& operator << (ostream& Output, const Student& stud); // 重载自定义数据类型的输出运算符  
};  
ostream& operator << (ostream& Output, const Student& stud)  
{  
    Output << stud.name << "的成绩为" << stud.mark;  
    return Output;  
}  
  
int main()  
{  
    const char* name = "zhangsan";  
    Student stud1(name, 86.6);  
    cout << stud1 << endl;  
}  

 

パラメータを転送するときの削除の問題を回避する

除去の問題の手順

#include <iostream>  
#include <string>  
using namespace std;  
  
class Person  
{  
private:  
    string name;  
    float weight;  
public:  
    Person(string name, float weight)  
    {  
        cout << "调用Person的构造函数" << endl;  
        this->name = name;  
        this->weight = weight;  
    }  
    ~Person()  
    {  
        cout << "调用Person的析构函数" << endl;  
    }  
    virtual void Display() // 虚函数  
    {  
        cout << this->name << "的体重为" << this->weight << endl;  
    }  
};  
  
class Student:public Person   
{  
private:  
    int StudName;  
public:  
    Student(int StudName, string name, float weight) :Person(name, weight)  
    {  
        this->StudName = StudName;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << "学生的学号为" << this->StudName << endl;  
    }  
};  
  
void ShowInf(Person person1)  
{  
    person1.Display();  
}  
  
int main()  
{  
    Person person1("张三", 51.6);  
    Student stud1(7, "李四", 52.3);  
    ShowInf(stud1); // 调用的是父类的Display函数并没有按照我们的需求调用子类中的虚函数  
} 

 

手続き上の問題の説明

要件に応じて、サブクラスのDisplay()関数は仮想関数として設定されます。サブクラスは親クラスに対してより具体的で詳細であるため、仮想関数の関数は「サブクラスの同じ名前の関数が親の同じ名前の関数をオーバーライドする」です。製品であるため、通常、親クラスの同じ名前の関数を仮想関数として宣言して、サブクラスによって具体化された同じ名前の関数を使用して、親クラスの古い関数を上書きする必要があります。

しかし、サブクラスオブジェクトをshowing()関数に渡すと、システムは親クラスを除くサブクラスのすべてを無慈悲に削除し、仮想関数を含む親クラスの継承された部分のみを保持します。私は何をすべきか?ディープコピーを使用して問題を解決することができます。

切除問題を解決するための手順

#include <iostream>  
#include <string>  
using namespace std;  
  
class Person  
{  
private:  
    string name;  
    float weight;  
public:  
    Person(string name, float weight)  
    {  
        cout << "调用Person的构造函数" << endl;  
        this->name = name;  
        this->weight = weight;  
    }  
    ~Person()  
    {  
        cout << "调用Person的析构函数" << endl;  
    }  
    virtual void Display() // 虚函数  
    {  
        cout << this->name << "的体重为" << this->weight << endl;  
    }  
};  
  
class Student:public Person   
{  
private:  
    int StudName;  
public:  
    Student(int StudName, string name, float weight) :Person(name, weight)  
    {  
        this->StudName = StudName;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << "学生的学号为" << this->StudName << endl;  
    }  
};  
  
void ShowInf(Person* person1)  
{  
    person1->Display();  
}  
  
int main()  
{  
    Person person1("张三", 51.6);  
    Student stud1(7, "李四", 52.3);  
    ShowInf(&stud1); // 调用的是父类的Display函数并没有按照我们的需求调用子类中的虚函数  
}  

 

小さな変更のみ

void ShowInf(Person* person1)  // 传入参数为父类指针
{  
    person1->Display();  // 调用子类的同名函数,虚函数功能被保留
}  

 

値をパラメーターに直接コピーするよりも、ポインターを渡す方がよいのはなぜですか?

ポインタの受け渡しはより柔軟です。正式なパラメータは親クラスへのポインタです。つまり、ポインタは親クラスのメンバー変数にのみアクセスできますが、「パラメータ値の転送」などの役に立たないものは直接削除されません。代わりに、すべてが変更されないままですが、私の親クラスポインターは、親クラスのメンバー(有効なメンバーのみ)のみを指し、サブクラスの新しいメンバーは、このポインターを介してアクセスできません。

通常、親ポインタが関数パラメータとして使用されるのはなぜですか?

ポインタはアドレスの同義語です。ポインタの種類を変更するときは、ポインタが指す有効メモリ空間のみを変更します。したがって、通常、親クラスポインタを関数パラメータとして選択し、継承関係にあるすべての子を渡すことができます。クラスポインタ。関数内で型を強制することによってのみ、サブクラスのすべての要素にアクセスできます。

破壊的なコピー

破壊的コピーは、スマートポインターがコピーされると、オブジェクトの所有権がターゲットポインターに転送され、元のポインターがリセットされるメカニズムです。名前から、破壊的コピーはデータの転送と同等であることがわかります。データが別の変数にコピーされると、コピーされたオブジェクトのポインターがリセットされます。

破壊的コピースマートポインタの基本的な実装原則

#include <iostream>  
#include <string>  
using namespace std;  
  
class Student  
{  
private:  
    string name;  
    float mark;  
public:  
    Student(string name, float mark)  
    {  
        cout << "调用Student的构造函数" << endl;  
        this->name = name;  
        this->mark = mark;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << this->name << "的成绩为" << this->mark << endl;  
    }  
};  
  
template<typename T>  
class Destroy_Pointer  
{  
private:  
    T* Dptr;  
public:  
    Destroy_Pointer() { Dptr = NULL; };  
    Destroy_Pointer(T* Dptr)  
    {  
        this->Dptr = Dptr;  
    }   
    Destroy_Pointer(Destroy_Pointer& Pointer)  
    {  
        if (this->Dptr != Pointer.Dptr)  
        {  
            this->Dptr = Pointer.Dptr;  
            Pointer.Dptr = NULL;  
        }  
    }  
    ~Destroy_Pointer()  
    {  
        if (this->Dptr != NULL)  
        {  
            delete this->Dptr;  
        }  
    }  
    Destroy_Pointer& operator = (Destroy_Pointer& Pointer)  
    {  
        if (this->Dptr != Pointer.Dptr)  
        {  
            this->Dptr = NULL;  
            this->Dptr = Pointer.Dptr;  
            Pointer.Dptr = NULL;  
            return *this;  
        }  
        else  
        {  
            this->Dptr = NULL;  
            Pointer.Dptr = NULL;  
            return *this;  
        }  
    }  
    T& operator * ()  
    {  
        return *Dptr;  
    }  
    T* operator -> ()  
    {  
        return this->Dptr;  
    }  
};  
  
int main()  
{  
    Student* stud1 = new Student("张三", 86.6);  
    Destroy_Pointer<Student> Dptr1(stud1), Dptr2;  
    Dptr2 = Dptr1;  
    //Dptr1->Display(); // 发生错误无法访问,是因为在"Dptr2 = Dptr1"时已经将Dptr1=null  
    Dptr2->Display();  
}  

 

破壊的なポインタを適用するときは注意してください

破壊的なコピーの後、コピーされたポインタはNULLに設定されています。この種のスマートポインタがソース参照を破壊することを考慮すると、これは、std :: vectorやその他の動的コレクションクラスなどのSTLコンテナでの使用にも不適切になります。これらのコンテナはコンテンツを内部的にコピーする必要があり、これによりポインタが無効になります。さまざまな理由から、プログラムで破壊的なコピーのスマートポインタを使用しないことをお勧めします。

メモリライブラリの破壊ポインタ「auto_ptr」を呼び出して、上記と同じ操作を実行します

#include <memory>  
#include <iostream>  
#include <string>  
using namespace std;  
  
class Student  
{  
private:  
    string name;  
    float mark;  
public:  
    Student(string name, float mark)  
    {  
        cout << "调用Student的构造函数" << endl;  
        this->name = name;  
        this->mark = mark;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << this->name << "的成绩为" << this->mark << endl;  
    }  
};  
  
int main()  
{  
    auto_ptr<Student> Dptr1(new Student("张三", 63.5)), Dptr2;  
    Dptr2 = Dptr1;  
    //Dptr1->Display(); // 不可以调用成员函数,因为Dptr1在复制时已经被摧毁  
    Dptr2->Display();   
}  

 

C ++ 11の新しいスマートポインター:メモリライブラリ内のunique_ptrスマートポインター

std :: unique_ptrはC ++ 11の新機能であり、コピーと割り当てができないため、auto_ptrとは少し異なります。unique_ptrは単純なスマートポインターですが、そのコピーコンストラクターと割り当て演算子はプライベートとして宣言されているため、コピーすることはできません。つまり、値で関数に渡すことも、他のポインターに割り当てることもできません。参照によりパラメーターを渡します。

つまり、unique_ptrは、コピーと割り当てによってソースのスマートポインタオブジェクトが無効にならないため、C ++ 11で廃止されたauto_ptrよりも安全です。オブジェクトが破棄されると解放されるため、簡単なメモリ管理に役立ちます。

Unique_ptrスマートポインタアプリケーションの例

#include <memory>  
#include <iostream>  
#include <string>  
using namespace std;  
  
class Student  
{  
private:  
    string name;  
    float mark;  
public:  
    Student(string name, float mark)  
    {  
        cout << "调用Student的构造函数" << endl;  
        this->name = name;  
        this->mark = mark;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << this->name << "的成绩为" << this->mark << endl;  
    }  
};  
  
int main()  
{  
    unique_ptr<Student> Dptr1(new Student("张三", 63.5)), Dptr2; // 只能在定义时对智能指针初始化  
    //Dptr2 = Dptr1;  // 禁止拷贝,赋值
    Dptr1->Display();
}  

 

ただし、移動移動セマンティクス(構文)を使用して、unique_ptrのコンテンツを移動できます。

#include <memory>  
#include <iostream>  
#include <string>  
using namespace std;  
  
class Student  
{  
private:  
    string name;  
    float mark;  
public:  
    Student(string name, float mark)  
    {  
        cout << "调用Student的构造函数" << endl;  
        this->name = name;  
        this->mark = mark;  
    }  
    ~Student()  
    {  
        cout << "调用Student的析构函数" << endl;  
    }  
    void Display()  
    {  
        cout << this->name << "的成绩为" << this->mark << endl;  
    }  
};  
  
int main()  
{  
    unique_ptr<Student> Dptr1(new Student("张三", 63.5)), Dptr2; // 只能在定义时对智能指针初始化  
    Dptr2 = move(Dptr1);  
    //Dptr1->Display(); // 由于move函数的使用,Dptr1中的内容被移动到Dptr2中,相当于Dptr1中已经被置空(Dptr1=NULL)  
    Dptr2->Display();  
  
}  

 

 

おすすめ

転載: blog.csdn.net/weixin_45590473/article/details/108195576