入門
C ++コンストラクタの実行は、(A)非継承
機能本体、初期化リストの数や順序を実行する前にメンバ変数を構築するために、すなわち、C ++コンストラクタの実行中に、連続の非存在下でのこの導入は、コンストラクタにありません実行順序への影響を引き起こす。
また、初期化リスト構造は、初期化リストは、可能な限り使用する理由変数のメンバーが分析方法に影響を与えるだろうと指摘しました。
相続の場合について、C ++のコンストラクタの実行は、第二を楽しみにしてください。
次のようにここでは環境に依存:
プラットフォーム:Windowsの10 64魏
コンパイラ:Visual Studioの2019
I.実行シーケンスコンストラクタ
1.1クラスを宣言
まず、私たちはクラスを宣言します。
// Dog.h
class Dog;
私たちは、クラスのインスタンスを作成する場合:
// main.cpp
Dog myDog = Dog( );
そして、コンパイラは、メモリ空間の一部に適用する、と呼ぶDog
、このインスタンスコンストラクタを。
コンストラクタ1.2を追加します。
私たちは、このクラスの全体のポイントをDianbu。
このクラスでは、コンストラクタ、デストラクタを追加します。
関数の本体では、各実行の順序を知って、デバッグプロセスで私たちを助けるために、ログを印刷します。
// Dog.h
class Dog
{
public:
Dog( )
{
std::cout << "Dog构造函数函数体"<< std::endl;
}
~Dog( ) { }
};
今再び実行します。
// main.cpp
std::cout << "Dog构造函数 开始" << std::endl;
Dog myDog = Dog( );
std::cout << "Dog构造函数 结束" << std::endl;
std::cout << "程序即将结束" << std::endl;
プログラムは、ログを印刷します。
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Dog构造函数函数体
3. Dog构造函数 结束
4. 程序即将结束
1.3メンバ変数を追加します
文明犬、すべての犬は、独自のカラーを持っている必要があります。
私たちはしなければならないDog
カラー追加collar
属性を。
注意:検証を容易にするために、我々は聞かせてcollar
、我々はそれを構築したとき、それがであることを判断できるように、ログをプリントアウトし、建設時にこのプロパティを取得する必要があるため、クラスのインスタンスです。
// Collar.h
class Collar
{
public:
// 缺省构造函数
Collar( )
{
std::cout << "Collar缺省构造函数" << std::endl;
}
};
今、私たちはDog
全体のメンバ変数を追加します。
// Dog.h
class Dog
{
public:
Dog( )
{
std::cout << "Dog构造函数函数体<< std::endl;
}
~Dog(){ }
private:
Collar collar_;
};
今再び実行します。
// main.cpp
std::cout << "Dog构造函数 开始" << std::endl;
Dog myDog = Dog(myCollar);
std::cout << "Dog构造函数 结束" << std::endl;
std::cout << "程序即将结束" << std::endl;
プログラムは、ログを印刷します。
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Collar缺省构造函数
3. Dog构造函数函数体
4. Dog构造函数 结束
5. 程序即将结束
現在の結論:
それが最初のメンバ変数を構成するクラスのインスタンスを作成する際に、その文は、コンストラクタ関数の本体を実行します。
上記のコードを観察し、我々はどこにも明示的に呼び出す必要はありませんCollar
コンストラクタを、それは次のようになります。
コンパイラは、あなたが完了助け
Collar
コンストラクタの呼び出しを。
しかし、このクラスは、複数のメンバ変数を持っている場合は、コンパイラへのメンバ変数の構造、それは?
メンバ変数1.4の建設順
今、私たちは、犬におもちゃを与えます。
// Toy.h
class Toy
{
public:
// 缺省构造函数
Toy( )
{
std::cout << "Toy缺省构造函数" << std::endl;
}
};
でDog
おもちゃの追加Toy
プロパティを。
// Dog.h
class Dog
{
// 构造和析构与1.3相同, 在此省略
private:
Collar collar_;
Toy toy_;
};
今、プログラムの実装では、ログを取得します:
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Collar缺省构造函数
3. Toy缺省构造函数
4. Dog构造函数函数体
5. // 其余日志与1.3相同, 在此省略
それは私たちの中に見ることができるclass Dog
の文、最初の文Collar
と宣言そして、そしてToy
、実際の実装プロセスは、最初に呼び出すことですCollar
、その後、デフォルトのコンストラクタを呼び出すToy
デフォルトコンストラクタを。
次のように改正する場合:
// Dog.h
class Dog
{
// 构造和析构与1.3相同, 在此省略
private:
Toy toy_; // 调换了位置
Collar collar_; // 调换了位置
};
ログにはなります:
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Toy缺省构造函数
3. Collar缺省构造函数
4. Dog构造函数函数体
5. // 其余日志与1.3相同, 在此省略
現在の結論:
クラスメンバ変数は、クラスの定義は、メンバ変数の宣言順序に従って構成され、クラスのコンストラクタ関数の本体よりも早くなるように構成されています。
1.5初期化シーケンスリストは、順序がメンバ変数の構造に影響を与えません。
我々は3つのテスト初期化リストを行います。
試験1:メンバ変数の初期化シーケンスのリストを一貫した順序と宣言する。
// Dog.h
class Dog
{
public:
Dog(const Collar& myCollar, const Toy& myToy)
: collar_(myCollar)
, toy_(myToy)
{
std::cout << "Dog构造函数函数体开始"<< std::endl;
std::cout << "Dog构造函数函数体结束" << std::endl;
}
private:
Collar collar_;
Toy toy_;
};
今、プログラムの実装では、ログを取得します:
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Collar缺省构造函数
3. Toy缺省构造函数
4. Dog构造函数函数体
5. // 其余日志与1.3相同, 在此省略
試験2:矛盾順序と変数宣言順序のメンバー初期化リスト。
// Dog.h
class Dog
{
public:
Dog(const Collar& myCollar, const Toy& myToy)
: toy_(myToy)
, collar_(myCollar)
{
std::cout << "Dog构造函数函数体开始"<< std::endl;
std::cout << "Dog构造函数函数体结束" << std::endl;
}
private:
Collar collar_;
Toy toy_;
};
今、プログラムの実装では、ログを取得します:
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Collar缺省构造函数
3. Toy缺省构造函数
4. Dog构造函数函数体
5. // 其余日志与1.3相同, 在此省略
いいえ変更ログません。
試験3:初期化リストの数は、メンバ変数の数より少ないです。
// Dog.h
class Dog
{
public:
Dog(const Collar& myCollar, const Toy& myToy)
: collar_(myCollar)
// 删除了toy_(myToy)
{
std::cout << "Dog构造函数函数体开始"<< std::endl;
std::cout << "Dog构造函数函数体结束" << std::endl;
}
private:
Collar collar_;
Toy toy_;
};
今、プログラムの実装では、ログを取得します:
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Dog构造函数 开始
2. Collar缺省构造函数
3. Toy缺省构造函数
4. Dog构造函数函数体
5. // 其余日志与1.3相同, 在此省略
いいえ変更ログません。
現在の結論:
数およびリストの初期化の順序は、メンバ変数施工順序に影響を及ぼしてはなりません。
建設順序は、まだクラスのコンストラクタより前の体の順。構造と機能のメンバ変数を宣言し、クラスの定義に基づいて構築されます。
1.6現在のコンストラクタの実行順序
- メモリスペースを開きます。
- メンバ変数の建設を開始するために、メンバ変数の宣言。
- 関数の本体に、ステートメントを実行します。
メンバ変数の構造でどのようにII。
メンバ変数代入のコンストラクタ本体で2.1、
今、私たちは、指定された示しcollar
ために、設定をCollar
別のコンストラクタを追加します。
// Collar.h
class Collar
{
public:
// 缺省构造函数
Collar( )
{
std::cout << "Collar缺省构造函数" << std::endl;
}
// 含参构造函数
Collar(std::string color)
{
std::cout << "Collar含参构造函数" << std::endl;
color_ = color;
}
// 拷贝构造函数, 这里直接使用了const引用, 是出于性能考虑. 如果用值拷贝, 会多构造一个collar出来, 然后再析构它.
Collar(const Collar& collar)
{
std::cout << "Collar拷贝构造函数" << std::endl;
this->color_ = collar.color_;
}
// 拷贝赋值运算符
Collar& operator = (const Collar& collar)
{
std::cout << "Collar拷贝赋值运算符" << std::endl;
this->color_ = collar.color_;
return *this;
}
// 析构函数
~Collar()
{
std::cout << "Collar析构函数" << std::endl;
}
private:
std::string color_;
};
我々はいくつかの主要な変更を行いました
- するには
Collar
、バンドの引数のコンストラクタを追加します。デフォルトのコンストラクタを容易に区別します。 - 追加
拷贝构造函数
。
// TODOは説明できません - 追加
拷贝赋值运算符
。
拷贝赋值运算符
実際に、私たちはしばしば「を使用=
」(より正確に」と説明したoperator =
「)、それはあなたが実行すると、すべてのクラスに存在するdog1 = dog2;
時間を、それが完了するために、この機能割り当てられた作業を呼び出すことです。
あなたがクラスであるかどうか定義は、この「の定義が無いoperator =
」機能は、コンパイラが自動的にそれを合成するためにあなたを助けているので、あなたが、それを使用することができ、。
C ++は、ユーザー自身がすることを可能にする「operator =
私が過負荷に、」このコードでオーバーロードされますこの機能は、追加のログを追加します。
変更Dog
のコンストラクタを:
// Dog.h
class Dog
{
public:
Dog(const Collar& myCollar)
{
std::cout << "Dog构造函数 函数体开始"<< std::endl;
// 将参数`collar`赋值给成员变量`collar_`
collar_= collar;
std::cout << "Dog构造函数 函数体结束" << std::endl;
}
~Dog(){ }
private:
Collar collar_;
};
主に私たちは、以下の変更を加えました:
- 犬は、パラメータを追加し、そのコンストラクタ宣言を改訂しました。
- パラメータのコンストラクタ関数の本体には
collar
メンバ変数に割り当てられましたcollar_
。 - このコンストラクタで他の関数を呼び出しますので、私たちは関数の本体の上部と下部に分析関数呼び出しチェーンのログを出力します。
修正main.cpp
Collar myCollar = Collar("yellow");
std::cout << "Dog构造函数 开始" << std::endl;
Dog myDog = Dog(myCollar);
std::cout << "Dog构造函数 结束" << std::endl;
std::cout << "程序即将结束" << std::endl;
次のように実際の印刷実行ログの後です。
// 日志, 每行开头的数字序号, 是我手动添加的, 数字后才是真实的日志.
1. Collar含参构造函数
2. Dog构造函数开始
3. ----Collar缺省构造函数
4. ----Dog构造函数函数体开始
5. --------Collar拷贝赋值运算符
6. ----Dog构造函数函数体结束
7. Dog构造函数结束
8. 程序即将结束
9. Collar析构函数"
10. Collar析构函数"
しかし、ログの2行目は、コンパイラを示したり、完了に役立つCollar
早期における暗黙のデフォルトコンストラクタの呼び出し、および呼び出しをDog
コンストラクタ呼び出し。
> 第一条日志, 调用`Collar`的含参构造函数, 构造出一个对象.
> 第二条日志, 标志着程序开始调用`Dog`构造函数.
> 第三条日志, 调用成员变量的`Collar`缺省构造函数, 将`collar_`构造出来.
> 第四条日志, 进入`Dog`的构造函数的函数体.
> 第五条日志, 调用拷贝赋值运算符, 将参数`myCollar`赋值给成员变量`collar_`;
> 第六条日志, `Dog`的构造函数的函数体结束.
> 第七条日志, 标志着`Dog`构造函数彻底结束.
> 第八条日志, 标志着程序即将结束, 开始进入析构阶段.
> 第九条日志, 在析构`Dog`实例的过程中, 会析构成员变量`collar_`, 执行`Collar`的析构函数.
> 第十条日志, 仍然是程序结束阶段, 会析构第一步建立的`myCollar`, 执行`Collar`的析构函数.
要約すると:
構成内Dog
のインスタンスの、5つのステップの合計含みますCollar
。
- パラメータ化されたコンストラクタ
- デフォルトコンストラクタ
- 代入演算子のコピー
- デストラクタ「デフォルト設定」
- デストラクタ「パラメータ化された構成」
2.2問題はどこにありますか?
ただ、5つのステップにまとめ、ステップ2と3は、無駄があります。
今、私たちは一人でこれらの2つのステップを参照してください。
最初のステップは:デフォルトコンストラクタ、構築に使用するcollar_
。オブジェクト
場合は、デフォルトのコンフィギュレーションプロセスを、collar_
非常に複雑なオブジェクトである、我々はそれがメンバ変数の数が含まれていると仮定して、各メンバーがいずれかの変数オブジェクトのクラスです、いずれかの構造。
このデフォルトの設定は、デフォルト値は、0 『か』 nullptrとして、通常は無用であることを覚えて、彼らにデフォルト値を与え、多くの時間、適切に構築され、各メンバ変数を過ごすことになります」。
そして、第2のステップ、コピー代入演算子へ:
このステップの前に、我々はmyCollar
、これは、来てパラメータとして渡さmyCollar
すでに構築されており、そのすべてのメンバ変数の値が正しいと意味があります今、私たちはそれをコピーcollar_
の仕上がりcollar_
作成、collar_
デフォルト値は11に覆われていたです。
今、あなたは問題を認識することがあります。
デフォルト値は、完全に余分最初のステップです!
我々は最初のステップの最初の部分を実装する必要がcollar_
構築されたオブジェクトを。
しかし、我々は最初のステップは、デフォルトの値の後半部分は必要ありません。
私たちは、直接、第二のステップを使用し、myCollar
値をコピーするためにcollar_
ライン上。
2.3初期化リスト
私たちは、あるDog.h
いくつかの変更:
// Dog.h
class Dog
{
public:
Dog(const Collar& myCollar)
: collar_(myCollar)
{
std::cout << "Dog构造函数函数体开始"<< std::endl;
std::cout << "Dog构造函数函数体结束" << std::endl;
}
~Dog(){ }
private:
Collar collar_;
};
主に私たちは、以下の変更を加えました:
- では
Dog
、コンストラクタ、直接、初期化リストを追加しmyCollar
初期化しますcollar_
。 - それは以来
collar_
、すでに初期化されている、機能代入演算子のコピーのボディを削除することができます。
その他の内容は同じまま、実行します。
1. Collar含参构造函数
2. Dog构造函数开始
3. Collar拷贝构造函数
4. Dog构造函数函数体开始
5. Dog构造函数函数体结束
6. Dog构造函数结束
7. 程序即将结束
8. Collar析构函数"
9. Collar析构函数"
比較ログで見つけることができます:
初期化リストと実行、Collar拷贝构造函数
1つのステップ、代わりの最後のランCollar缺省构造函数
+ 拷贝赋值运算符
2つのステップ。
回避Collar缺省构造
、不要なデフォルトを回避することができます。
現在の結論:
クラスのメンバ変数のために、私たちはクラスのコンストラクタを入力する前に工事を完了します。
メンバ変数の初期化リスト場合は、変数の型のコピーコンストラクタを実行します。
メンバ変数の初期化がリストにない場合、それは意志実行型の変数のデフォルトコンストラクタ。
2.4可能な限り、初期化リスト
初期化リストを使用し、それがパフォーマンスの問題の主な原因です。
あなたは、初期化、余分な通話時間を完了するために、コンストラクタの本体ではなく、初期化リストを使用しない場合は、以前の我々の分析によると缺省构造
。
以下のような組み込み型についてint
、double
コンパイラが最適化されているため、初期化リストと体を初期化するコンストラクタで、パフォーマンスの違いは、素晴らしいではありません。
しかし、クラス型のため、パフォーマンスの違いは、巨大な、数回かもしれません。
もう一つの理由は、あなたが初期化リストを使用しなければならない場合があるということです。
定数メンバは、定数のみを割り当てることができないので、それが初期化リストの内側に配置されなければならない初期化することができます。
参照型は、参照は、初期化時に定義する必要があり、再割り当てすることはできませんので、また、内部の初期化リストで書きます。
初期化リストを初期化するデフォルトコンストラクタを呼び出す必要がありますが、直接初期化するために、コピーコンストラクタを呼び出すことはできませんので、クラス型のデフォルトコンストラクタは、ありません。
注:変数が特定の値、ゼロ値または特定の意味を使用していない値を知らないために、そのようなint型を使用すると0
、使用のstd ::文字列型""
、ポインタの型を使用nullptr
。
3つの実行シーケンスコンストラクタ
- メモリスペースを開きます。
- メンバ変数の建設を開始するために、メンバ変数の宣言。
- メンバ変数の初期化リスト場合は、コピーコンストラクタの変数の型を実行します。
- メンバ変数がリストに初期化されていない場合は、デフォルトコンストラクタの変数の型を実行します。
- 関数の本体に、ステートメントを実行します。