にゃ〜
1. コンストラクター
C++ オブジェクト指向の学習では、デフォルト コンストラクターとカスタム コンストラクターがあるコンストラクターについてよく理解します。次のクラスのフレームワークがあるとします。
1.1 デフォルトのコンストラクター
class Human{
public:
void eat();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
この時点で印刷するとき、クラスにコンストラクターが定義されていないため、コンパイラは自動的にデフォルトのコンストラクターを呼び出します。このコンストラクターにはパラメーター リストも関数本体もなく、空のコンストラクターですHuman(){}
。カスタム コンストラクターがない場合、コンパイラーは自動的にコンストラクターを呼び出します。プログラム内でコンストラクターがカスタマイズされると、デフォルトのコンストラクターは時間とともに無効になり、再度呼び出されなくなります。。
1.2 カスタムのデフォルトコンストラクター
class Human{
public:
Human();
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
string Human::getName(){
return name;
}
int Human::getAge(){
return age;
}
void Human::eat(){
cout << "eat" << endl;
}
Human::Human(){
name = "zhongguo";
age = 9000;
}
int main(){
Human hu;
cout << hu.getName() << endl;//zhongguo
cout << hu.getAge() <<endl;//9000
return 0;
}
コンストラクタはカスタマイズされていますが、パラメータはありません。これを一般にカスタムデフォルトコンストラクタと呼びます。もちろん、これをカスタムコンストラクタと呼んでも問題はありません。実は、上記の書き方を一つにまとめて、プロトタイプとコンストラクタHuman()の定義をまとめてpublicに置くこともできます。
class Human{
public:
Human(){
name = "zhongguo";
age = 9000;
}
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
1.3 パラメータを使用してコンストラクターをカスタマイズする
このメソッドは、コンストラクターを作成する最も一般的な方法です。
class Human{
public:
Human(string name,int age);
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
string Human::getName(){
return name;
}
int Human::getAge(){
return age;
}
void Human::eat(){
cout << "eat" << endl;
}
Human::Human(string name,int age){
this->name = name;
this->age = age;
}
int main(){
Human hu("zhongguo",9000);
cout << hu.getName() << endl;//zhongguo
cout << hu.getAge() <<endl;//9000
return 0;
}
2 番目に、コピー コンストラクターの基本的な使用法
同様に、プログラム中にコピーコンストラクタを定義していない場合、コンパイラはデフォルトのコピーコンストラクタを呼び出しHuman hu("zhongguo",1000000)
ます。たとえば、次のコード:Human hu2 = hu
Human hu3(hu)
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);//自定义的有参构造函数
Human(const Human&);//自定义的拷贝构造函数
string getName();
int getAge();
int getSalary();
private:
string name = "zhonguo";
int age = 28;
int salary;
};
//自定义有参构造函数,并且参数不全
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";
}
//拷贝构造函数都是引用
//执行Human h2 = h1; 这句时会被调用,man.name 相当于h1.name只是赋值给了h2.name
Human::Human(const Human& man) {
cout << "调用自定义的拷贝构造函数" << endl;
name = man.name;
age = man.age;
salary = man.salary;
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
//下面h2的三个成员变量的值得打印与h1完全相同
cout << "==============================" << endl;
cout << "姓名:" << h2.getName() << endl;
cout << "年龄: " << h2.getAge() << endl;
cout << "薪资:" << h2.getSalary() << endl;
system("pause");
return 0;
}
上記のコードではコピーコンストラクターをカスタマイズしていますが、実際にはこの場合デフォルトのコピーコンストラクターを使用するのと同じですが、場合によってはデフォルトのコピーコンストラクターを使用するのは危険です。
自動生成されたコンストラクター (合成コピー コンストラクターとも呼ばれる) は「浅いコピー」、つまりビット コピーであるため
浅いコピーの場合、クラスのメンバーがポインタの場合、エラーが発生します。
以下の例を参照してください。
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);
//Human(const Human&); //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”
string getName();
int getAge();
int getSalary();
void setAddr(const char* newAddr);
const char* getAddr();
private:
string name = "Unknown";
int age = 28;
int salary;
//成员变量中直接定义一个指针
char* addr;
};
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";//将name从unknow换成无名
addr = new char[64];
//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
strcpy_s(addr, 64, "China");
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
void Human::setAddr(const char* newAddr) {
if (!newAddr) {
return;
}
strcpy_s(addr, 64, newAddr);
}
const char* Human::getAddr() {
return addr;
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
h1.setAddr("长沙");
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
system("pause");
return 0;
}
結果は次のとおりです。
自動的に生成されたコピー コンストラクターを使用する場合に問題が見つかりましたHuman h2 = h1
。
クラス内にポインタメンバが存在する場合、それは一緒に固定されるのと等価であり、h1 の変更は h2 の変更を引き起こします。
C++ strcpy_s と strncpy_s の使用方法
解決策: カスタム コピー コンストラクターで、「ディープ コピー」を使用します。
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);
Human(const Human &man); //如果不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”
string getName();
int getAge();
int getSalary();
void setAddr(const char* newAddr);
const char* getAddr();
private:
string name = "Unknown";
int age = 28;
int salary;
//成员变量中直接定义一个指针
char* addr;
};
Human::Human(const Human & man) {
cout << "调用了自定义的拷贝构造函数" << endl;
age = man.age;
name = man.name;
salary = man.salary;
//使用自定义的拷贝构造函数进行深拷贝
addr = new char[64];
strcpy_s(addr,64,man.addr);
}
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";//将name从unknow换成无名
addr = new char[64];
//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
strcpy_s(addr, 64, "China");
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
const char* Human::getAddr() {
return addr;
}
void Human::setAddr(const char* newAddr) {
if (!newAddr) {
return;
}
strcpy_s(addr, 64, newAddr);
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
h1.setAddr("长沙");
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
system("pause");
return 0;
}
上記の結果から、h2 のアドレスは変更されていないことがわかります。
2.1 浅いコピーと深いコピー(原理と違い)
深いコピーと浅いコピーの違いはコピーコンストラクターに従って分析できます
通常、デフォルトのコピー コンストラクターは、標準のコピー コンストラクターが参照型であるため、元のオブジェクトの参照を新しいオブジェクトに直接コピーする浅いコピーを使用します。このとき、新しいオブジェクトと古いオブジェクトは同じ値を指します。参照は同じです。はい、元のオブジェクトの値を変更すると、新しいオブジェクトの値も変更されます
また、ディープコピーとは、新しいオブジェクトを作成し、元のオブジェクトのさまざまな属性の値をコピーすることですが、このとき、元のオブジェクトが再割り当てされても、新しいオブジェクトの値は変更されません。
上記の例によると、メンバー オブジェクトにポインターがある場合は、ディープ コピーを使用する必要があります。
浅いコピー
ディープコピー