🌻天国にいけるC++言語入門🌻 進化し続けるオブジェクト指向プログラミング ver3.2307
コピーコンストラクタが設定されているとプログラムが実行できない場合があります strcpy_s関数が用いられたプログラムの例を観察してみましょう
strcpy_s関数が正常に動作するようにコピーコンストラクタを設定してみます
コピーコンストラクタが設定されているとプログラムが実行できない場合があります strcpy_s関数が用いられたプログラムの例を観察してみましょう
🌞 🌞 🌞 🌞 🌞 🌞 🌞
コピーコンストラクタの仕組み わかったぁ?😊
🌞 🌞 🌞 🌞 🌞 🌞 🌞
solarplexuss「まあまあかな」
🌞 🌞 🌞 🌞 🌞 🌞 🌞
ところで
コピーコンストラクタが設定されていると
🌞オブジェクト宣言,初期化を同時に実行して🌞
オブジェクトを生成しても
そのオブジェクトが初期化されないため
プログラムがうまく実行できない場合があります
//🌞🌞🌞基本的にコピーコンストラクタが設定されている状態で
オブジェクト宣言,初期化を同時に実行する場合は
つまり
オブジェクト宣言を行いながらオブジェクトにオブジェクトを代入している場合は
生成されるオブジェクトが代入されるオブジェクトによって初期化されるわけではありません
ええっ?と思われた方😊
最後までこのエピソードをご覧ください🌞🌞🌞//
オブジェクトが初期化されないということは
オブジェクトのメンバ変数が初期化されないということなので
オブジェクトのメンバ変数に値が代入されないことになります
価の代入されないオブジェクトのメンバ変数が
プログラム内で用いられていると
プログラムがうまく実行できないことがあるというわけなんだね
そこで
そのようなタイプの例として
🌞オブジェクト宣言,初期化を同時に実行して🌞
オブジェクトを生成しても
そのオブジェクトが初期化されないため
(オブジェクトのメンバ変数が初期化されないため)
strcpy_s関数が機能しないという例
を観察してみよう!
それでは😊
手始めに まず
コンストラクタも
コピーコンストラクタも設定されていない状態で
strcpy_s関数が用いられている次のプログラムをご覧ください
//🌞コンストラクタを設定しないとコピーコンストラクタを設定することはできません
🌞 🌞 🌞 🌞 🌞 🌞 🌞
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
};
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
にゃこ
にゃこ
ねこねこ
ねこねこ
にゃん
にゃん
ソーラー「このプログラムでは
まず
SuutitoMoji b;
b.x = 1;
により
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.xに1が代入されています
そして
b.i = new char[50];
を実行することにより
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iを用いて
動的にメモリ領域を確保しています
このとき
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iには
動的に確保したメモリ領域の先頭のメモリのアドレスが
代入されることになります
次に
strcpy_s(b.i,50, "にゃこ");
が実行されると
動的に確保されたメモリ領域に
文字列データ
"にゃこ"
が格納されることになります
クラスSuutitoMojiに
💖コピーコンストラクタが設定されていない💖
この状態で
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji a = b;
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.xには
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.xの格納している
1が
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iの格納している
動的に確保されたメモリ領域の先頭のメモリのアドレスが
格納されることになります
素直な仕組みですね
次に
cout << a.i << "\n";
cout << b.i << "\n";
が実行されますね
cout << a.i << "\n";
は
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
に格納されている文字列データを
コマンドプロンプト画面に表示する命令文
で
cout << b.i << "\n";
は
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
に格納されている文字列データを
コマンドプロンプト画面に表示する命令文です
動的に確保されたメモリ領域には
文字列データ
"にゃこ"
が格納されていて
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
はともに
動的に確保したメモリ領域の先頭のメモリのアドレス番号を格納しているので
cout << a.i << "\n";
cout << b.i << "\n";
の実行結果は
にゃこ
にゃこ
となります
この状態で
strcpy_s(a.i, 50, "ねこねこ");
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iの格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
つまり
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iの格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
つまり
動的に確保したメモリ領域
に文字列データ
"ねこねこ"
は格納されることになります
次に
cout << a.i << "\n";
cout << b.i << "\n";
が実行されますね
cout << a.i << "\n";
は
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
に格納されている文字列データを
コマンドプロンプト画面に表示する命令文
で
cout << b.i << "\n";
は
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
に格納されている文字列データを
コマンドプロンプト画面に表示する命令文です
💖コピーコンストラクタが設定されていない状態では💖
b.i = new char[50];
が実行された後
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji a = b;
が実行されると
a.i=b.i
となっているので
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
は
ともに
b.iによって動的に確保したメモリ領域の先頭のメモリのアドレス番号を格納することになり
cout << a.i << "\n";
と
cout << b.i << "\n";
は
等しく動的に確保されたメモリ領域に格納されている文字列データを
コマンドプロンプト画面に表示する命令文となります
cout << a.i << "\n";
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
つまり
動的に確保されたメモリ領域に格納された文字列データ
"ねこねこ”
が
コマンドプロンプト画面に表示され
cout << b.i << "\n";
が実行されると
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域
つまり
動的に確保されたメモリ領域に格納された文字列データ
"ねこねこ”
が
コマンドプロンプト画面に表示されることになります
次に
strcpy_s(b.i, 50, "にゃん");
が実行されると
b.iによって動的に確保されたメモリ領域に
文字列データ"にゃん"
が格納されます
このとき
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
は
ともに
文字列データ"にゃん"を格納した
動的に確保したメモリ領域の先頭のメモリのアドレスを格納しています
ですので
cout << a.i << "\n";
cout << b.i << "\n";
の
実行結果は
にゃん
にゃん
になります
めでたし
めでたし」
マックス「おう やるじゃんか
やったな ソーラー
この説明は大したものだな
さすがに 驚いたぞ!」
ソーラー「ははっ ここで
コピーコンストラクタをこのプログラムに付け加えてみたいと思います
さあ
コピーコンストラクタの挙動に注目してほしいな
(コピーコンストラクタを設定する場合はコンストラクタも同時に設定しておく必要があります)」
マックス「なんか 面白いことが起きるのか?」
ソーラー「ふふっさあっ
起こるかな?
ところで
クラスSuutitoMojiに
コピーコンストラクタの
クラスSuutitoMojiのメンバ関数宣言が設定されていると
💖🌞クラスSuutitoMoji型のオブジェクト宣言、初期化🌞💖
が実行されるときに
自動的に
実行されるのが
💖クラスSuutitoMoji型のオブジェクトのコピーコンストラクタ💖でした
覚えているかな?」
マックス「はぇ?なんのことだったか?」
ソーラー「
今のプログラムでは
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji
が実行されています
ですので
もし
今のプログラムの
クラスSuutitoMojiの
クラス宣言内で
💖コピーコンストラクタのメンバ関数宣言💖
を設定していれば
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji
が実行されると
クラスSuutitoMoji型のオブジェクト
クラスSuutitoMoji型のオブジェクト
いよいよきたかな?
このとき!
SuutitoMoji
が実行されても
💖オブジェクトaはオブジェクトbによって初期化されない💖
というところが💖注目💖ポイントなんです」
solarplexuss「はぇぇ?なんでぇ?」
マックス「そうだった、そういや
オブジェクトaのコピーコンストラクタ
が実行されるときは
コピーコンストラクタの定義の😊引数😊となっている
クラス型の参照変数宣言にオブジェクトbが代入されたものが実行されるんだったな」
ソーラー「そこなんです」
🌞 🌞 🌞 🌞 🌞 🌞
そこっ そこっ
うふふふ
🌞 🌞 🌞 🌞 🌞 🌞
solarplexuss 「まさか
SuutitoMoji
は初期化の命令文でなく
オブジェクトaのコピーコンストラクタが実行される命令文ってことぉ?」
ソーラー「そうなんだ
そうだね
実際にそのことを確かめるために
まずは・・・
コンストラクタ
と
コピーコンストラクタが設置されている
クラスSuutitoMojiの作製からはじめてみよっかな
SuutitoMojiのクラス宣言に
コピーコンストラクタのメンバ関数宣言を
を加えて
コピーコンストラクタの定義を設定してみます
こんなかんじかな?
おおっと もう
忘れかかってた!
クラスSuutitoMojiに
コピーコンストラクタを設定する場合は
必ず
コンストラクタを設定しておく必要があるんだったね
そこで
以下のように
クラスSuutitoMojiに
コンストラクタ
と
コピーコンストラクタを
設定してみます
class SuutitoMoji{
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji(){
cout<<”コンストラクタが実行されました”<<"\n";
}
//🌞👆コンストラクタの定義です
SuutitoMoji::SuutitoMoji(SuutitoMoji& c){
cout<<”コピーコンストラクタが実行されました”<<"\n";
}
//🌞👆コピーコンストラクタの定義です
ソーラー
「👆この
コンストラクタ
と
コピーコンストラクタ
が設定されたクラスSuutitoMojiが
用いられたプログラムを実行してみます」
そのプログラムはこちらです
👇
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji() {
cout << "コンストラクタが実行されました" << "\n";
}
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
コンストラクタが実行されました
コピーコンストラクタが実行されました
マックス「むぅ?
コンストラクタが実行されました
コピーコンストラクタが実行されました
だけが表示されている・・・・・・
気のせいか?
なんか実行結果の表示が足りないような( ^ω^)・・・」
ソーラー「このプログラムでは
まず
bのクラスSuutitoMoji型のオブジェクト宣言
SuutitoMoji b;
の実行により
クラスSuutitoMoji型のオブジェクトbが生成されます
このとき
クラスSuutitoMoji型のオブジェクトbのコンストラクタが実行されるため
コマンドプロンプト画面に
コンストラクタが実行されました
が表示されることになります
次に
b.x = 1;
により
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.xに数値データが代入されています
次は
b.iだね
この時点ではまだ
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i には"ねこねこ"のような文字列データを格納しているメモリ領域の先頭のメモリのアドレスは代入されていない所に注目してほしいな
そう
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i はアドレスを格納するポインタ変数なのですが
まだ初期化されていません
次に
b.i = new char[50];
の実行により
動的にメモリ領域が確保され
動的に確保したメモリ領域の先頭のメモリのアドレスが
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i に格納されています
b.iの初期化が完了しましたね
次に
strcpy_s(b.i, 50, "にゃこ");
の実行により
b.iに格納されているアドレスのメモリを先頭とした動的に確保したメモリ領域に
文字列データ"にゃこ"が格納されることになります
次に
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji a = b;
の実行により
🌞クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタ🌞
が実行されることになります
このとき
コピーコンストラクタの定義
👇
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
の
🌞SuutitoMoji& cに🌞
🌞クラスSuutitoMoji型のオブジェクトbが代入された状態で🌞
cout << "コピーコンストラクタが実行されました" << "\n";
が実行されることになり
コピーコンストラクタが実行されました
が
コマンドプロンプト画面に表示されます
このとき
aのクラスSuutitoMoji型のオブジェクト宣言、クラスSuutitoMoji型のオブジェクトbによる初期化
SuutitoMoji a = b;
の実行により
クラスSuutitoMoji型のオブジェクトbが代入される先は
💖SuutitoMoji& cであって💖
💖SuutitoMoji a でない💖
というところに注目だね😊
さてさて
今までの段階で
コマンドプロンプト画面に
コンストラクタが実行されました
コピーコンストラクタが実行されました
が表示されることになります
ここまではいいんだけどな・・・・・
なぜか
この後には何も表示されず
コマンドプロンプト画面に
コンストラクタが実行されました
コピーコンストラクタが実行されました
までしか表示されないのが問題だね
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
に続く以降の命令文
👇
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
の実行結果はコマンドプロンプト画面に表示されていないね」
マックス「?
さっきの
コンストラクタ
も
コピーコンストラクタ
も
設定されていないプログラムと違い
コンストラクタ
と
コピーコンストラクタをセットで設定することにより
うまくプログラムが実行できなくなっている
ってわけか・・
なんで
コンストラクタが実行されました
コピーコンストラクタが実行されました
しか
コマンドプロンプト画面に表示されていないぃ?
さっきの一番最初のプログラムはちゃんと実行できただろう
今のプログラムは
一番最初のプログラムの
クラスSuutitoMojiに
😊コンストラクタ😊
と
😊コピーコンストラクタ😊
が新たに設定されただけだろう?」
ソーラー「そうですね
クラスSuutitoMojiに
おまけで
コンストラクタ
と
コピーコンストラクタ
をセットで設定したんだけど
どうしてこうなっちゃったかな?
このプログラムの流れをもう1度追ってみようよ
そうしたら
なぜ
コンストラクタが実行されました
コピーコンストラクタが実行されました
までしか
コマンドプロンプト画面に表示されないのかがわかるかもしれないね
このプログラムを最初から追ってみると・・・
まず
SuutitoMoji b;
の実行により
クラスSuutitoMoji型のオブジェクトbが生成されています
このとき
クラスSuutitoMoji型のオブジェクトbのコンストラクタが実行されるので
オブジェクトのコンストラクタが実行されました
が
コマンドプロンプト画面に表示されることになります
そして
b.x = 1;
により
クラスSuutitoMoji型のオブジェクトbのメンバ変数
b.x には1が代入されています
次に
b.i = new char[50];
の実行により
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iを用いて
動的にメモリ領域を確保しています
このとき
動的に確保したメモリ領域の先頭のメモリのアドレスが
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iに格納されることになります
次に
strcpy_s(b.i,50, "にゃこ");
を実行して
文字列データ
"にゃこ"
を
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが格納しているアドレスのメモリを先頭とする動的に確保したメモリ領域に格納しています
次あたりからが注目ポイントかな?
次に
aのクラスSuutitoMoji型のオブジェクト宣言、同時にクラスSuutitoMoji型のオブジェクトbによる初期化
💖SuutitoMoji a = b;💖
が実行されると
💖クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが実行されるので💖
オブジェクトのコピーコンストラクタが実行されました
が
コマンドプロンプト画面に表示されることになります」
マックス「お、ここまではいいじゃないか」
ソーラー
「
aのクラスSuutitoMoji型のオブジェクト宣言、同時にクラスSuutitoMoji型のオブジェクトbによる初期化
💖SuutitoMoji a = b;💖
の実行に伴い
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが実行されるとき
クラスSuutitoMoji型のオブジェクトbがコピーコンストラクタの定義
👇
SuutitoMoji::SuutitoMoji(SuutitoMoji& c){
cout<<”コピーコンストラクタが実行されました”<<"\n";
}
の参照変数宣言
SuutitoMoji& c
に代入されます
ですので
SuutitoMoji a = b;
により
生成されるクラスSuutitoMoji型のオブジェクトaには
クラスSuutitoMoji型のオブジェクトbが代入されず
クラスSuutitoMoji型のオブジェクトaは初期化されないことになります
つまり
クラスSuutitoMoji型のオブジェクトaのメンバ変数
a.x
a.i
には値が代入されない
初期化されない
ということになるね
その状態で
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
の実行となるわけですが
まず最初に
cout << a.i << "\n";
を実行しようとしても
SuutitoMoji a = b;
が実行される時の
オブジェクトaのコピーコンストラクタの働きでは
クラスSuutitoMoji型のオブジェクトa
は
クラスSuutitoMoji型のオブジェクトbによって初期化されなかったので
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには何も値(アドレス)が代入されていない状態となっています
ですので
🌞🌞🌞クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレス番号の付いたメモリを先頭とするメモリ領域に格納されている文字列データを表示する命令文である🌞🌞🌞
cout << a.i << "\n";
を実行しようとしても
実行することはできないってわけなんだ
さすがに
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに何も値(アドレス番号)が代入されていない状態で
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに格納されているアドレスを先頭とするメモリ領域に格納されている文字列データを表示する命令文
cout << a.i << "\n";
を実行しようとしても
コマンドプロンプト画面に文字列を表示することはできないね
そのため
ここでプログラムの実行が止まってしまう
というわけなんだね」
マックス
「コピーコンストラクタが設定されている場合に
うまくプログラムが実行できない例ってわけか
aのクラスSuutitoMoji型のオブジェクト宣言、同時にクラスSuutitoMoji型のオブジェクトbによる初期化
💖SuutitoMoji a = b;💖
を実行して
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが
実行されても
クラスSuutitoMoji型のオブジェクトaにはクラスSuutitoMoji型のオブジェクトbが
代入されない
ので
生成されるクラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには何も値(アドレス)が代入されていないか・・・
んじゃなあ
しょうがないじゃないか・・・
ふうむ
原因は
💖コピーコンストラクタが設定されている場合に💖
SuutitoMoji a = b;
を実行した場合は
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが
実行されるが
クラスSuutitoMoji型のオブジェクトaにはクラスSuutitoMoji型のオブジェクトbが代入されず
クラスSuutitoMoji型のオブジェクトaは初期化されない
つまり
クラスSuutitoMoji型のオブジェクトaのメンバ変数
a.x
a.i
は
初期化されない
ということにあるわけだ
価の代入されていない変数をプログラム内でもちいると
問題がおこるというわけだ
このプログラムでは
😊アドレスが代入されていないポインタ変数が😊
プログラム内で使用されているので
問題がおこるってことか」
int(イント)
「
💖コピーコンストラクタが設定されている場合に💖
SuutitoMoji a = b;
を実行する場合は御注意くださ~いね😊😊」
マックス「( ̄∇ ̄;)ハッハッハ
ということは
とりあえず
cout << a.i << "\n";
が実行できるようにすれば
まあいいんじゃないか?
さあ、どうしてみようか?」
ソーラー「そうだ
cout << a.i << "\n";
が実行できるようにするには
a.iが初期化
つまり
アドレスが代入されていればいいんじゃないかな?」
マックス「なんだ そんなことか
当たり前か
なら
SuutitoMoji a = b;
のあと
cout << a.i << "\n";
の前に
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iを用いて
メモリ領域に文字列データを格納する
a.i="なーおん";//👈😊ココね もう無理やりです(笑)
//👆🌞今は懐かしいです ポインタ変数に文字列データを代入する機能は現在のVisual Studioでは削除されています
を設定、実行して
メモリ領域に文字列データ"なーおん"を格納し
そのメモリ領域の先頭のメモリのアドレスを
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに格納されるようにしておけばいいだけのことなんじゃないか?
cout << a.i << "\n";
を実行すれば
コマンドプロンプト画面に
なーおん
が表示されるはずだろう
楽勝だぜぇ いぇい!(^^)!」
そのプログラムはこちらです
👇
#include <iostream>
using namespace std;
class SuutitoMoji {
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji() {
cout << "コンストラクタが実行されました" << "\n";
}
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
a.i = "なーおん";//👈😊ココね もう強引です
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
コンストラクタが実行されました
コピーコンストラクタが実行されました
なーおん
にゃこ
マックス「やったぜい
ぐははははっ
ぐはっ ぐはっ ぐははははっ
どうだぁ
俺の力は! げへへへへ」
int(イント)「?ねえ ねえ ちょっと?」
マックス「ぐははははっ どうだあっ
int(イント)よ!
感激しているようだな」
int(イント)「命令文は
以下のようになっていて
👇
SuutitoMoji a = b;
a.i = "なーおん";
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
👆
cout << a.i << "\n";
cout << b.i << "\n";
が
3回
もちいられているけど
ビルド実行結果に表示されているのは
コンストラクタが実行されました
コピーコンストラクタが実行されました
なーおん
にゃこ
だけじゃない?
ほんとうは
コンストラクタが実行されました
コピーコンストラクタが実行されました
なーおん
にゃこ
ねこねこ
にゃこ
ねこねこ
にゃん
みたいな感じで
表示されなければいけないんじゃないの?」
マックス「ぬわぁぬぃぃぃぃぃ・・・・・・・」
マックスの握りこぶしに血管が浮かび上がる
マックス「ぐぬぬ・・・
なんでおもったように実行できんんんんんん」
ソーラー「このプログラムでは
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
a.i = "なーおん";//👈ここね 強引です
cout << a.i << "\n";
cout << b.i << "\n";
までは
実行されてますが
その次の命令文
strcpy_s(a.i, 50, "ねこねこ");
が実行されていません
strcpy_s(a.i, 50, "ねこねこ");
を実行することはできないんです
なぜなら
🌞以前のstrcpy_sに関するエピソードでも解説されたように🌞
strcpy_s関数の第1引数は第3引数の文字列データを受け入れることができる状態になっている必要があるからなんです
第3引数の
文字列データ"ねこねこ"は
💖配列に格納されている文字列データ(ねこねこ)💖
の
💖アドレス💖
を表しています
ですので
第1引数には
その
💖配列に格納されている文字列データ(ねこねこ)💖
を格納することができる
ポインタ変数が
記述されている必要があります
この場合
そのことが可能なポインタ変数は
💖配列のアドレスを格納しているポインタ変数💖
もしくは
💖動的に確保したメモリ(配列)のアドレスを格納しているポインタ変数💖
となります
しかし
今、実行された命令文
strcpy_s(a.i, 50, "ねこねこ");
のオブジェクトa.iのメンバ変数a,iは
a.i = "なーおん";//👈ここね 強引です
(👆現在のVisual Studioではこの命令文を実行することはできません)
が実行されても
💖配列のアドレスを格納しているポインタ変数💖
もしくは
💖動的に確保したメモリ(配列)のアドレスを格納しているポインタ変数💖
でもないので
strcpy_s(a.i, 50, "ねこねこ");
を実行することができず
strcpy_s(a.i, 50, "ねこねこ");
の後に続く命令文
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
が実行されることはありません
となると
なんとか
😊strcpy_s(a.i, 50, "ねこねこ");😊
を実行するには・・・
そうだね
1つの方法として
strcpy_s(a.i, 50, "ねこねこ");
が実行される前に
a.i = "なーおん";//👈ここね 強引です
(👆現在のVisual Studioではこの命令文を実行することはできません)
を実行するのではなく
代わりに
a.i=new char[50]:
を実行して
動的にメモリを確保して
a.iを
💖動的に確保したメモリ(配列)のアドレスを格納しているポインタ変数💖
にしておくという方法があるね
つまり
a.iを
文字列データ"ねこねこ"を受け入れることができるポインタ変数に設定するんです
これで
strcpy_s(a.i, 50, "ねこねこ");
は実行されることになるね
他の方法は・・・
そうだね・・・
う~ん
もしくは
a.i = "なーおん";//👈ここね 強引です
(👆現在のVisual Studioではこの命令文を実行することはできません)
を実行するのではなく
代わりに
ポインタ変数dを用いて
d.i="ねこねこ";
(👆現在のVisual Studioではこの命令文を実行することはできません)
を実行し
文字列データ"ねこねこ"
をメモリ領域に格納
そのメモリ領域の先頭のメモリのアドレスをd.iに格納しておいて
strcpy_s(a.i, 50, "ねこねこ");
の代わりに
strcpy_s(a.i, 50,d.i);
を実行するという方法もあるね
strcpy_s(a.i, 50, "ねこねこ");
を直接実行するのはあきらめるというわけだね
(この場合
strcpy_s関数の
第1引数と第3引数に同じ型のポインタ変数が用いられることになります)
今回は
a.i = "なーおん";//👈ここね 強引です
(👆現在のVisual Studioではこの命令文を実行することはできません)
を実行する代わりに
1つ目の方法である
a.iをもちいて
a.i=new char[50]:
を実行して
動的にメモリ領域を確保する方法
を用いて
strcpy_s(a.i, 50, "ねこねこ");
を
実行できるようにしてみたいと思います
そのプログラムはこちらです
👇
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji() {
cout << "コンストラクタが実行されました" << "\n";
}
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a=b;
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
コンストラクタが実行されました
コピーコンストラクタが実行されました
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
にゃこ
ねこねこ
にゃこ
ねこねこ
にゃん
マックス「お、うまくビルド実行できたじゃないか
ちゃんと
strcpy_s(a.i, 50, "ねこねこ");
に続く命令文
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
が実行されたじゃないか
やったな!
と
おもったら
なんだ?
このプログラムの実行結果の
👇
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
は?
」
ソーラー「???
んん???
んんんんん!?
まず
このプログラムでは
SuutitoMoji b;
の実行により
クラスSuutitoMoji型のオブジェクトbが生成されています
このとき
クラスSuutitoMoji型のオブジェクトbのコンストラクタが実行されるので
オブジェクトのコンストラクタが実行されました
が
コマンドプロンプト画面に表示されることになります
そして
b.x = 1;
により
クラスSuutitoMoji型のオブジェクトbのメンバ変数
b.x には1が代入されています
次に
b.i = new char[50];
の実行により
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iを用いて
動的にメモリ領域を確保しています
次に
strcpy_s(b.i,50, "にゃこ");
を実行して
文字列データ
"にゃこ"
を
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iの格納しているアドレスのメモリを先頭とするメモリ領域
すなわち
b.iを用いて動的に確保したメモリ領域
に格納しています
e
次に
aのクラスSuutitoMoji型のオブジェクト宣言、同時にクラスSuutitoMoji型のオブジェクトによる初期化
SuutitoMoji a = b;
が実行されると
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが実行されるので
オブジェクトのコピーコンストラクタが実行されました
が
コマンドプロンプト画面に表示されることになります
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが実行されるとき
クラスSuutitoMoji型のオブジェクトbが
コピーコンストラクタの定義
👇
SuutitoMoji::SuutitoMoji(SuutitoMoji& c){
cout<<”コピーコンストラクタが実行されました”<<"\n";
}
の参照変数宣言
SuutitoMoji& c
に代入された状態で
cout<<”コピーコンストラクタが実行されました”<<"\n";
が実行されます
ですので
SuutitoMoji a = b;
により
生成されるクラスSuutitoMoji型のオブジェクトaには
クラスSuutitoMoji型のオブジェクトbが代入されず
クラスSuutitoMoji型のオブジェクトaは初期化されないことになります
この次の命令文は・・・・・
おや おや・・・
先程のプログラムと違い
今のプログラムでは
a.i = "なーおん";//👈😊ココね
が用いられず
ここで
a.iを用いて
a.i = new char[50];
が実行されているね
a.i = new char[50];
の実行により
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには
動的に確保したメモリ領域の先頭のメモリのアドレスが格納されていますが
この段階では
まだ
動的に確保されたメモリ領域には文字列データは格納されていません
つまり
動的に確保されたメモリ領域は文字列データによって初期化されていません
ですので
この状態で
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレスのメモリを先頭とするメモリ領域に格納されている文字列データを表示する命令文
cout << a.i << "\n";
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレスのメモリを先頭とするメモリ領域
つまり
a.iによって動的に確保されたメモリ
に格納されている文字列データが表示されることになりますが
動的に確保されたメモリ領域は初期化されていないので
動的に確保されたメモリ領域が初期化されていないときに
動的に確保されたメモリ領域に元から格納されているデータ
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
が
コマンドプロンプト画面に表示されることになります
それにしても
へへへって面白い実行結果ですね
a.i = "なーおん";//👈😊ココですね
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");//👈😊ココです
cout << a.i << "\n";
を実行したときは
strcpy_s(a.i, 50, "ねこねこ");//👈😊ココです
が実行できないのに
a.i = new char[50];//👈😊ココですね
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");//👈😊ココです
cout << a.i << "\n";
を実行したときは
一応
strcpy_s(a.i, 50, "ねこねこ");
が実行できたというわけです
次に
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a=b;
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");//🌞実行できました
cout << a.i << "\n";
に続く命令文
cout <<b.i << "\n";
が実行されると
さきに
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
が実行されていたので
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが格納しているアドレスのメモリを先頭とするメモリ領域
つまり
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iによって動的に確保されたメモリ領域
に格納されている文字列データ
にゃこ
が
コマンドプロンプト画面に表示されることになります」
マックス「
a.i = new char[50];
の実行により
a.iによって動的に確保されたメモリ領域には文字列データが代入されていない、初期化されていないので
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
に続く
cout << a.i << "\n";
の
実行結果として
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
がコマンドプロンプト画面に表示されたというわけか
その後
以下を見てもわかるように
SuutitoMoji a=b;
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
👆
strcpy_s(a.i, 50, "ねこねこ");
が実行されることによって
cout << a.i << "\n";
の実行結果は
ねこねこ
が表示されることになったわけだ
」
ソーラー「
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
がコマンドプロンプト画面に表示されることになりました
a.iを用いて動的にメモリを確保する
a.i = new char[50];
を実行することにより
動的に確保したメモリ領域の先頭のメモリのアドレスを
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに格納したことにより
strcpy_s関数の
第1引数にa.i
第3引数に"ねこねこ"
を用いた
strcpy_s(a.i, 50, "ねこねこ");
が実行できるようになります
strcpy_s(a.i, 50, "ねこねこ");
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iが格納しているアドレスのメモリを先頭とするメモリ領域
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iによって
動的に確保されたメモリ領域に
文字列データ”ねこねこ"
は格納されることになります
とりあえず
strcpy_s(a.i, 50, "ねこねこ");
が実行できずに
プログラムの実行が止まるようなことはありません
プログラムは引き続き実行されることになります
strcpy_s(a.i, 50, "ねこねこ");
の
次に
続く命令文
cout << a.i << "\n";
cout << b.i << "\n";
の
実行結果に
御注目下さい
一番最初のプログラムでは
実行結果は
ねこねこ
ねこねこ
となっていましたが
今のプログラムの場合
実行結果は
ねこねこ
にゃこ
になっていますね。
cout << a.i << "\n";
は
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに格納されているアドレスの付いたメモリを先頭とするメモリ領域に
格納されている文字列データを表示する命令文で
cout << b.i << "\n";
は
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iに格納されているアドレスの付いたメモリを先頭とするメモリ領域に
格納されている文字列データを表示する命令文です
このプログラムでは
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには
a.i=new char[50];
を実行して動的に確保したメモリ領域の先頭のメモリのアドレスが格納されていて
a.iを用いて動的に確保したメモリ領域には文字列データ
"ねこねこ"
が格納されています
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iには
b.i=new char[50];
を実行して
動的に確保したメモリ領域の先頭のメモリのアドレスが格納されていて
b.iを用いて動的に確保したメモリ領域には文字列データ
"にゃこ"
が格納されています
ですので
cout << a.i << "\n";
cout << b.i << "\n";
のビルド実行結果は
ねこねこ
にゃこ
になるわけです
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
に格納されているアドレスは異っているというわけです
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iが
同じアドレスを格納していれば
cout << a.i << "\n";
cout << b.i << "\n";
の実行結果に
先ほどのプログラムのように
同じ文字列
ねこねこ
ねこねこ
が表示されますが
このプログラムでは
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
の格納しているアドレスは
異なっているんです
ですから
cout << a.i << "\n";
cout << b.i << "\n";
の実行結果に異なる文字列
ねこねこ
にゃこ
が
表示されるのですね
次に
strcpy_s(b.i, 50, "にゃん");
が実行されると
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iを用いて動的に確保されたメモリ領域には
文字列データ
"ねこねこ"
が格納されたままで
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.iを用いて動的に確保されたメモリ領域には
文字列データ
"にゃん"
が格納されることになるので
cout << a.i << "\n";
cout << b.i << "\n";
のビルド実行結果は
ねこねこ
にゃん
になります
もし
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
と
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
の格納しているアドレスが同じなら
strcpy_s(b.i, 50, "にゃん");
が実行されると
cout << a.i << "\n";
cout << b.i << "\n";
のビルド実行結果は
にゃん
にゃん
になるはずですが
このプログラムではそうなりませんね😊
それはともかく
このプログラムで
一歩、改良、前進したのは
aのクラスSuutitoMoji型のオブジェクト宣言、同時にクラスSuutitoMoji型のオブジェクトによる初期化
SuutitoMoji a = b;
の実行においては
クラスSuutitoMoji型のオブジェクトaのコピーコンストラクタが実行されるので
クラスSuutitoMoji型のオブジェクトa
に
クラスSuutitoMoji型のオブジェクトb
が代入されず
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.i
に
クラスSuutitoMoji型のオブジェクトbのメンバ変数b.i
が代入されないということになっても
a.i = new char[50];
を実行して
a.iに動的に確保したメモリ領域の先頭のメモリのアドレスを代入したことにより
a.iに動的に確保したメモリ領域の先頭のメモリのアドレスを代入していなければ実行できない
strcpy_s(a.i, 50, "ねこねこ");
が実行できるようになったことかな(*^▽^*)
つまり
a.i = new char[50];
を設定、実行した意味は
strcpy_s(a.i, 50, "ねこねこ");
が実行できるようすることにあるんだね
a.i="なーおん";
では
strcpy_s(a.i, 50, "ねこねこ");
を実行することは無理だったので
a.i = new char[50];
を設定、実行したんだね
ただ
a.i = new char[50];
を実行して動的にメモリ領域を確保した段階では
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iに
動的に確保したメモリ領域の先頭のメモリのアドレスが代入されるんだけど
動的に確保したメモリ領域には何も文字列データが代入されていないので
cout << a.i << "\n";
の実行結果は
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
がコマンドプロンプト画面に表示されることにるんだね
」
マックス「う~ん
とりあえず
プログラムの実行がとまらなくなったのは
なかなか
いいんじゃないか
あとは
プログラムの実行結果が
コンストラクタが実行されました
コピーコンストラクタが実行されました
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
にゃこ
ねこねこ
にゃこ
ねこねこ
にゃん
の
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
が表示されないようにすればいいんだろう
プログラムを最初からみていくと
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a=b;
a.i = new char[50];
cout << a.i << "\n";👈
となっているが
このcout << a.i << "\n";👈
実行結果が
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
となっているわけか・・・
マックス「コピーコンストラクタが設定されていない最初のプログラムはどうなっていたっけか?
👇
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
};
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a = b;
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
にゃこ
にゃこ
ねこねこ
ねこねこ
にゃん
にゃん
マックス「👆こんなんだったか
そもそもこの最初のプログラムで
SuutitoMoji a=b;
を実行したのは
明らかに
クラスSuutitoMoji型のオブジェクトa
に
クラスSuutitoMoji型のオブジェクトb
が代入されるようにして
a.i=b.i;
を実行するためだろう
それが
コピーコンストラクタを設定すると
クラスSuutitoMoji型のオブジェクトa
に
クラスSuutitoMoji型のオブジェクトb
が代入されず
a.i=b.i;
が実行できなくなったわけだ
まあ、なんだ
コピーコンストラクタの設定なんていらないんじゃないか?
だいたい
コピーコンストラクタの仕組みをよく知らない人が
コンストラクタ
コピーコンストラクタ
が追加でセットされた
今の新しいプログラムを作製したとする
👇
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji() {
cout << "コンストラクタが実行されました" << "\n";
}
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a=b;
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
コンストラクタが実行されました
コピーコンストラクタが実行されました
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
にゃこ
ねこねこ
にゃこ
ねこねこ
にゃん
マックス「コピーコンストラクタの仕組みを知らない人が
このようにプログラムを作製した場合
SuutitoMoji a=b;
が実行されると
クラスSuutitoMoji型のオブジェクトa
に
クラスSuutitoMoji型のオブジェクトb
が代入されて
a.i=b.i;
が実行されると
ほぼ100パーセント思うじゃんか?
(そうでしょう 実際にはa.i=b.i;は実行されません)
それが自然だろ
a.i=b.iが実行されるとおもっているので
a.iにはb.iの格納しているアドレスが代入されるとおもうわけだ
となると
cout << a.i << "\n";👈
を実行すれば
a.iの格納しているアドレスのついたメモリを先頭とするメモリ領域
イコール
b.iの格納しているアドレスのついたメモリを先頭とするメモリ領域
つまり
b.iによって動的に確保されたメモリ領域に格納されている文字列データ
"にゃこ"
の
にゃこ
がコマンドプロンプト画面に表示される!!と思うだろう
おしいぃ
だが実際には
SuutitoMoji a=b;
が実行されると
🌞コピーコンストラクタの参照変数宣言SuutitoMoji& c🌞
に
クラスSuutitoMoji型のオブジェクトbが代入されるため
クラスSuutitoMoji型のオブジェクトa
は
クラスSuutitoMoji型のオブジェクトb
によって初期化されることはないので
👇
#include <iostream>
using namespace std;
class SuutitoMoji{
public:
int x;
public:
char* i;
public:
SuutitoMoji();
//🌞コンストラクタのメンバ関数宣言です
public:
SuutitoMoji(SuutitoMoji& c);
//🌞コピーコンストラクタのメンバ関数宣言です
};
SuutitoMoji::SuutitoMoji() {
cout << "コンストラクタが実行されました" << "\n";
}
SuutitoMoji::SuutitoMoji(SuutitoMoji& c) {
cout << "コピーコンストラクタが実行されました" << "\n";
}
int main() {
SuutitoMoji b;
b.x = 1;
b.i = new char[50];
strcpy_s(b.i, 50, "にゃこ");
SuutitoMoji a=b;
a.i = new char[50];//👈a.i="な~おん";の代わりに持ってきました
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(a.i, 50, "ねこねこ");
cout << a.i << "\n";
cout << b.i << "\n";
strcpy_s(b.i, 50, "にゃん");
cout << a.i << "\n";
cout << b.i << "\n";
return 0;
}
プログラムの実行結果
コンストラクタが実行されました
コピーコンストラクタが実行されました
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
にゃこ
ねこねこ
にゃこ
ねこねこ
にゃん
👆以上のようなプログラムの実行結果となるわけだ
さらに
もしもコピーコンストラクタの定義をうまく変更して
SuutitoMoji a=b;
の実行により
a.i=b.iが実行されるようにしたとしても
a.i = new char[50];
が実行されると
a.iは
ふたたび文字列データによって
初期化されていない
動的に確保されたメモリ領域の先頭のメモリのアドレスを格納することになり
cout << a.i << "\n";👈
の実行結果として
初期化されていない
動的に確保されたメモリ領域に格納されているデータ
(ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・のようなもの)
が
表示されるんだろ~な~
まあ
それはもしもの話だが・・・
ようは
コピーコンストラクタが設定されている場合
SuutitoMoji a=b;
の実行により
クラスSuutitoMoji型のオブジェクトa
が
クラスSuutitoMoji型のオブジェクトb
によって初期化されるというわけではなく
a.i=b.iが
実行されるとは限らない😊
a.i=b.iが
実行されるかどうかは
コピーコンストラクタの定義次第だと
コピーコンストラクタが設定されていると
SuutitoMoji a=b;
が実行されたとしても
a.i=b.i;
は実行されない場合がある
今のこのプログラムで
a.i = new char[50];
をとりのぞいてしまうと
SuutitoMoji a=b;
につづく命令文
strcpy_s(a.i, 50, "ねこねこ");
が実行できなくなるので
プログラムの実行結果は
コンストラクタが実行されました
コピーコンストラクタが実行されました
が表示されるだけで
終わってしまうというわけだ」
ソーラー「今までのお話をまとめると
コピーコンストラクタが設定されている場合
SuutitoMoji a=b;
の実行により
クラスSuutitoMoji型のオブジェクトa
が
クラスSuutitoMoji型のオブジェクトb
によって初期化されることはなく
必ずしも
a.i=b.iが
実行されるとは限りません
a.i=b.iが
実行されるかどうかは
コピーコンストラクタの定義次第です
ですので
a.i=b.iが
実行されない場合
クラスSuutitoMoji型のオブジェクトaのメンバ変数a.iには何もアドレスが代入されていないので
strcpy_s(a.i, 50, "ねこねこ");
を実行することはできなくなります
そこで
strcpy_s(a.i, 50, "ねこねこ");
の前に
a.i="なーおん";
を設定したとしても
このポインタ変数a.iでは
strcpy_s(a.i, 50, "ねこねこ");
を実行することはできないんだね
そこで
a.i="なーおん";
の代わりに
a.i = new char[50];
をもってくると
strcpy_s(a.i, 50, "ねこねこ");
を実行することができるようになったんだ
だけど
a.i = new char[50];
を実行して動的にメモリを確保しただけでは
動的に確保したメモリに何も文字列データが格納されていないので
a.iによって動的に確保された文字列データを表示する命令文
cout << a.i << "\n";👈
を実行すると
何も文字列データが代入されないときに
動的に確保されたメモリ領域に格納されているデータ
ヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘヘ・・・・
が
表示されるんだね」
新規登録で充実の読書を
- マイページ
- 読書の状況から作品を自動で分類して簡単に管理できる
- 小説の未読話数がひと目でわかり前回の続きから読める
- フォローしたユーザーの活動を追える
- 通知
- 小説の更新や作者の新作の情報を受け取れる
- 閲覧履歴
- 以前読んだ小説が一覧で見つけやすい
アカウントをお持ちの方はログイン
ビューワー設定
文字サイズ
背景色
フォント
組み方向
機能をオンにすると、画面の下部をタップする度に自動的にスクロールして読み進められます。
応援すると応援コメントも書けます