目次
値をパラメーターに直接コピーするよりも、ポインターを渡す方がよいのはなぜですか?
通常、親ポインタが関数パラメータとして使用されるのはなぜですか?
メモリライブラリの破壊ポインタ「auto_ptr」を呼び出して、上記と同じ操作を実行します
C ++ 11の新しいスマートポインター:メモリライブラリ内の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();
}