Loose-Info.com
Last Update 2024/01/09
TOP - 各種テスト - C++ - クラス

概要

クラスオブジェクトの作成テスト
struct および class キーワード
クラスメンバに関するテスト(ネストされたクラス)
メンバ関数
thisポインタ
静的メンバ
ビットフィールド
共用体(union)
匿名共用体
ローカルクラス
クラスメンバのアクセス制御
派生クラスと基底クラスに関するアクセス制御
フレンド(friend)
コンストラクタ
デフォルトコンストラクタ
コピーコンストラクタ(暗黙的)
暗黙的および明示的コピーコンストラクタの比較テスト
ヒープ領域に確保した配列へのポインタを含むクラスのコピーコンストラクタ
移動コンストラクタ
コピー代入演算子
移動代入演算子
デストラクタ
暗黙的コピーコンストラクタ使用時の明示的デストラクタの挙動
コンストラクタにおける真偽を結果とする演算子によるクラスメンバの初期化


クラスオブジェクトの作成テスト


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } // structによるクラス宣言 struct Samp_st { // クラスメンバはデフォルトでpublic int i; int j; }; int main() { // クラスのオブジェクトsaの作成 Samp_st sa = {1, 2}; // sa内のクラスメンバの値 std::cout << "sa.i = " << sa.i << std::endl; std::cout << "sa.j = " << sa.j << std::endl; // saの格納アドレス std::cout << "&sa = " << &sa << std::endl; // saのサイズ std::cout << "sizeof(sa) = " << sizeof(sa) << std::endl; // saのバイト列 byteseq(reinterpret_cast<unsigned char *>(&sa), sizeof(sa), "sa"); std::cout << std::endl; // クラスのオブジェクトsbの作成 Samp_st sb = {3, 4}; // sb内のクラスメンバの値 std::cout << "sb.i = " << sb.i << std::endl; std::cout << "sb.j = " << sb.j << std::endl; // sbの格納アドレス std::cout << "&sb = " << &sb << std::endl; // sbのバイト列 byteseq(reinterpret_cast<unsigned char *>(&sb), sizeof(sb), "sb"); std::cout << std::endl; // saをsbに代入 sb = sa; // 代入後のsb内のクラスメンバの値 std::cout << "sb.i = " << sb.i << std::endl; std::cout << "sb.j = " << sb.j << std::endl; // sbの格納アドレス std::cout << "&sb = " << &sb << std::endl; // 代入後のsbのバイト列 byteseq(reinterpret_cast<unsigned char *>(&sb), sizeof(sb), "sb"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out sa.i = 1 sa.j = 2 &sa = 0x7ffca9dc1528 sizeof(sa) = 8 sa = 01 00 00 00 02 00 00 00 sb.i = 3 sb.j = 4 &sb = 0x7ffca9dc1520 sb = 03 00 00 00 04 00 00 00 sb.i = 1 sb.j = 2 &sb = 0x7ffca9dc1520 sb = 01 00 00 00 02 00 00 00

struct および class キーワード


sample.cpp
#include <iostream> // structによるクラス宣言 struct Samp_st { // デフォルトがpublic Samp_st(int i, int j) : m(i), n(j) {} int getm() { return m; } int getn() { return n; } private: int m; int n; }; // classによるクラス宣言 class Samp_cl { // デフォルトがprivate int m; int n; public: Samp_cl(int i, int j) : m(i), n(j) {} int getm() { return m; } int getn() { return n; } }; int main() { // クラスのオブジェクトstの作成 Samp_st st(1, 2); std::cout << "st.getm() = " << st.getm() << std::endl; std::cout << "st.getn() = " << st.getn() << std::endl; // クラスのオブジェクトclの作成 Samp_cl cl(3, 4); std::cout << "cl.getm() = " << cl.getm() << std::endl; std::cout << "cl.getn() = " << cl.getn() << std::endl; std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out st.getm() = 1 st.getn() = 2 cl.getm() = 3 cl.getn() = 4

クラスメンバに関するテスト(ネストされたクラス)


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Samp { // ネストされた(入れ子の)クラスの宣言 struct Ncls { bool flg; int n; // コンストラクタの宣言 Ncls(); }; bool flg; int n; Ncls nc; // ネストされたクラスのオブジェクト enum { en_0, en_1, en_2 }; // 列挙型 // コンストラクタ Samp() : flg(true), n(en_2) { nc.flg = false; } }; // クラス定義外でのネストされたクラスのコンストラクタ定義 Samp::Ncls::Ncls() { n = en_0; } int main() { // クラスSampのオブジェクトを作成 Samp s; // ネストされたクラスNclsのオブジェクトを作成 Samp::Ncls sn; // ネストされたクラスNclsのオブジェクトのメンバの値を変更 sn.flg = true; // Sampクラスのメンバの列挙型でNclsのメンバの値を指定 sn.n = Samp::en_1; std::cout << "s.flg = " << s.flg << std::endl; std::cout << "s.n = " << s.n << std::endl; std::cout << "s.nc.flg = " << s.nc.flg << std::endl; std::cout << "s.nc.n = " << s.nc.n << std::endl; std::cout << "sn.flg = " << sn.flg << std::endl; std::cout << "sn.n = " << sn.n << std::endl; std::cout << std::endl; // クラスSamp、Nclsのサイズ std::cout << "sizeof(s) = " << sizeof(s) << std::endl; std::cout << "sizeof(sn) = " << sizeof(sn) << std::endl; // クラスSamp、Nclsの格納バイト列 byteseq(reinterpret_cast<unsigned char *>(&s), sizeof(s), "s"); byteseq(reinterpret_cast<unsigned char *>(&sn), sizeof(sn), "sn"); std::cout << std::endl; // SampのNcls型メンバにオブジェクトsnを代入 s.nc = sn; byteseq(reinterpret_cast<unsigned char *>(&s), sizeof(s), "s"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out s.flg = 1 s.n = 2 s.nc.flg = 0 s.nc.n = 0 sn.flg = 1 sn.n = 1 sizeof(s) = 16 sizeof(sn) = 8 s = 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 sn = 01 00 00 00 01 00 00 00 s = 01 00 00 00 02 00 00 00 01 00 00 00 01 00 00 00

メンバ関数


sample.cpp
#include <iostream> class Smpl { int n; public: Smpl(int i) : n(i) {} // 定義はクラス外 void calc(int i); // クラス内で定義される関数 int getinc() { return ++n; } // const指定されたメンバ関数(クラスメンバの変更不可) // 非静的メンバ関数はcv修飾子での宣言が可能 int getn() const { return n; } }; // クラス外で定義される関数 void Smpl::calc(int i) { n = i * 2; } int main() { Smpl s(2); std::cout << "s.getinc() = " << s.getinc() << std::endl; std::cout << "s.getn() = " << s.getn() << std::endl; std::cout << std::endl; s.calc(5); std::cout << "s.calc(5) を実行" << std::endl; std::cout << "s.getn() = " << s.getn() << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out s.getinc() = 3 s.getn() = 3 s.calc(5) を実行 s.getn() = 10

thisポインタ


sample.cpp
#include <iostream> class Smpl { int n; double d; public: Smpl() : n(1), d(1.1) {} Smpl& faddval(int x) { n += x; d += static_cast<double>(x) * 1.2; // *thisによって関数が呼び出されるオブジェクトを返す return *this; } // クラスメンバの表示関数 void fview() { std::cout << "n = " << n << std::endl; std::cout << "d = " << d << std::endl; } }; int main() { Smpl s; s.fview(); std::cout << std::endl; // 関数の演算結果が反映された関数を呼び出したオブジェクトへのリファレンスを作成 Smpl& sr = s.faddval(2); // リファレンスをからメンバ関数を使用してクラスメンバを表示 sr.fview(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out n = 1 d = 1.1 n = 3 d = 3.5

静的メンバ


sample.cpp
#include <iostream> class Smpl { // 静的データメンバの宣言 static int n0; int n; public: Smpl(int x) : n(x) {} void setn(int x) { n = x; } void setn0(int x) { n0 = x; } int getval() { return n - n0; } // 静的メンバ関数 static void setn0_st(int x) { n0 = x; } }; // 静的データメンバの定義 int Smpl::n0 = 0; int main() { std::cout << "クラスオブジェクトs1、s2をコンストラクタにより生成" << std::endl; Smpl s1(3); Smpl s2(0); // 各オブジェクトの静的・非静的メンバ間の演算を行うメンバ関数を実行 std::cout << "s1.getval() = " << s1.getval() << std::endl; std::cout << "s2.getval() = " << s2.getval() << std::endl; std::cout << std::endl; std::cout << "クラスオブジェクトs1、s2のメンバ関数からデータメンバの値を変更" << std::endl; s1.setn(5); s2.setn(2); std::cout << "s1.getval() = " << s1.getval() << std::endl; std::cout << "s2.getval() = " << s2.getval() << std::endl; std::cout << std::endl; std::cout << "クラスオブジェクトs1のメンバ関数から静的データメンバの値を変更" << std::endl; s1.setn0(1); std::cout << "s1.getval() = " << s1.getval() << std::endl; std::cout << "s2.getval() = " << s2.getval() << std::endl; std::cout << std::endl; std::cout << "クラスオブジェクトs2のメンバ関数から静的データメンバの値を変更" << std::endl; s2.setn0(-1); std::cout << "s1.getval() = " << s1.getval() << std::endl; std::cout << "s2.getval() = " << s2.getval() << std::endl; std::cout << std::endl; std::cout << "静的メンバ関数から静的データメンバの値を変更" << std::endl; Smpl::setn0_st(2); std::cout << "s1.getval() = " << s1.getval() << std::endl; std::cout << "s2.getval() = " << s2.getval() << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out クラスオブジェクトs1、s2をコンストラクタにより生成 s1.getval() = 3 s2.getval() = 0 クラスオブジェクトs1、s2のメンバ関数からデータメンバの値を変更 s1.getval() = 5 s2.getval() = 2 クラスオブジェクトs1のメンバ関数から静的データメンバの値を変更 s1.getval() = 4 s2.getval() = 1 クラスオブジェクトs2のメンバ関数から静的データメンバの値を変更 s1.getval() = 6 s2.getval() = 3 静的メンバ関数から静的データメンバの値を変更 s1.getval() = 3 s2.getval() = 0

ビットフィールド


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { // ビットフィールド unsigned short n0 : 4; unsigned short n1 : 2; unsigned short n2 : 6; unsigned short n3 : 4; Smpl(unsigned short x); }; Smpl::Smpl(unsigned short x) { // 引数から各ビットフィールドを抽出 n0 = x & 0b1111; n1 = (x >> 4) & 0b11; n2 = (x >> 6) & 0b111111; n3 = (x >> 12) & 0b1111; } int main() { // クラスオブジェクトの生成 Smpl s1(0xFFFF); // 1111 111111 11 1111 Smpl s2(0x1051); // 0001 000001 01 0001 Smpl s3(0x9879); // 1001 100001 11 1001 // Smplオブジェクトのサイズ std::cout << "sizeof(Smpl) = " << sizeof(Smpl) << std::endl; std::cout << std::endl; // Smplオブジェクトのバイト列 byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; std::cout << std::dec; // 引数から各ビットフィールドの値 std::cout << "1111 111111 11 1111" << std::endl; std::cout << "s1.n0 = " << s1.n0 << std::endl; std::cout << "s1.n1 = " << s1.n1 << std::endl; std::cout << "s1.n2 = " << s1.n2 << std::endl; std::cout << "s1.n3 = " << s1.n3 << std::endl; std::cout << std::endl; std::cout << "0001 000001 01 0001" << std::endl; std::cout << "s2.n0 = " << s2.n0 << std::endl; std::cout << "s2.n1 = " << s2.n1 << std::endl; std::cout << "s2.n2 = " << s2.n2 << std::endl; std::cout << "s2.n3 = " << s2.n3 << std::endl; std::cout << std::endl; std::cout << "1001 100001 11 1001" << std::endl; std::cout << "s3.n0 = " << s3.n0 << std::endl; std::cout << "s3.n1 = " << s3.n1 << std::endl; std::cout << "s3.n2 = " << s3.n2 << std::endl; std::cout << "s3.n3 = " << s3.n3 << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out sizeof(Smpl) = 2 s1 = FF FF s2 = 51 10 s3 = 79 98 1111 111111 11 1111 s1.n0 = 15 s1.n1 = 3 s1.n2 = 63 s1.n3 = 15 0001 000001 01 0001 s2.n0 = 1 s2.n1 = 1 s2.n2 = 1 s2.n3 = 1 1001 100001 11 1001 s3.n0 = 9 s3.n1 = 3 s3.n2 = 33 s3.n3 = 9

共用体(union)


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } union Smpl { // 4つの整数型メンバを宣言 unsigned char c; unsigned short s; unsigned int i; unsigned long l; }; int main() { Smpl u; // unsigned char型メンバに値を代入 u.c = 0x11; byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u.c(0x11) "); // unsigned short型メンバに値を代入 u.s = 0x2222; byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u.s(0x2222) "); // unsigned int型メンバに値を代入 u.i = 0x44444444; byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u.i(0x44444444) "); // unsigned long型メンバに値を代入 u.l = 0x8888888888888888; byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u.l(0x8888888888888888)"); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out u.c(0x11) = 11 00 00 00 00 00 00 00 u.s(0x2222) = 22 22 00 00 00 00 00 00 u.i(0x44444444) = 44 44 44 44 00 00 00 00 u.l(0x8888888888888888) = 88 88 88 88 88 88 88 88

匿名共用体


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Stct { // クラス内の匿名共用体 union { unsigned int i; unsigned long l; }; unsigned int n; }; namespace Nam1 { // 名前空間内の匿名共用体(static指定必須) static union { unsigned int i; unsigned long l; }; } void f() { // 関数内の匿名共用体(static指定必須) union { unsigned int i; unsigned long l; }; std::cout << "関数内の匿名共用体メンバへの代入(unsigned long 型)" << std::endl; l = 0xFFFFFFFFFFFFFFFF; std::cout << "l = " << l << std::endl; byteseq(reinterpret_cast<unsigned char *>(&l), sizeof(l), "l"); std::cout << "関数内の匿名共用体メンバへの代入(unsigned int 型)" << std::endl; i = 0x44444444; std::cout << "i = " << i << std::endl; byteseq(reinterpret_cast<unsigned char *>(&l), sizeof(l), "l"); byteseq(reinterpret_cast<unsigned char *>(&i), sizeof(i), "i"); std::cout << std::endl; } // グローバル名前空間内の匿名共用体(static指定必須) static union { unsigned int i; unsigned long l; }; int main() { std::cout << std::uppercase << std::hex; Stct st; st.n = 0x88888888; std::cout << "Stctクラス内の匿名共用体メンバへの代入(unsigned long 型)" << std::endl; st.l = 0xFFFFFFFFFFFFFFFF; std::cout << "st.n = " << st.n << std::endl; std::cout << "st.l = " << st.l << std::endl; byteseq(reinterpret_cast<unsigned char *>(&st), sizeof(st), "st"); std::cout << "Stctクラス内の匿名共用体メンバへの代入(unsigned int 型)" << std::endl; st.i = 0x44444444; std::cout << "st.i = " << st.i << std::endl; byteseq(reinterpret_cast<unsigned char *>(&st), sizeof(st), "st"); std::cout << std::endl; std::cout << "名前空間内の匿名共用体メンバへの代入(unsigned long 型)" << std::endl; Nam1::l = 0xFFFFFFFFFFFFFFFF; std::cout << "Nam1::l = " << Nam1::l << std::endl; byteseq(reinterpret_cast<unsigned char *>(&Nam1::l), sizeof(Nam1::l), "Nam1::l"); std::cout << "名前空間内の匿名共用体メンバへの代入(unsigned int 型)" << std::endl; Nam1::i = 0x44444444; std::cout << "Nam1::i = " << Nam1::i << std::endl; byteseq(reinterpret_cast<unsigned char *>(&Nam1::l), sizeof(Nam1::l), "Nam1::l"); byteseq(reinterpret_cast<unsigned char *>(&Nam1::i), sizeof(Nam1::i), "Nam1::i"); std::cout << std::endl; std::cout << "グローバル名前空間内の匿名共用体メンバへの代入(unsigned long 型)" << std::endl; l = 0xFFFFFFFFFFFFFFFF; std::cout << "l = " << l << std::endl; byteseq(reinterpret_cast<unsigned char *>(&l), sizeof(l), "l"); std::cout << "グローバル名前空間内の匿名共用体メンバへの代入(unsigned int 型)" << std::endl; i = 0x44444444; std::cout << "i = " << i << std::endl; byteseq(reinterpret_cast<unsigned char *>(&l), sizeof(l), "l"); byteseq(reinterpret_cast<unsigned char *>(&i), sizeof(i), "i"); std::cout << std::endl; f(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out Stctクラス内の匿名共用体メンバへの代入(unsigned long 型) st.n = 88888888 st.l = FFFFFFFFFFFFFFFF st = FF FF FF FF FF FF FF FF 88 88 88 88 00 00 00 00 Stctクラス内の匿名共用体メンバへの代入(unsigned int 型) st.i = 44444444 st = 44 44 44 44 FF FF FF FF 88 88 88 88 00 00 00 00 名前空間内の匿名共用体メンバへの代入(unsigned long 型) Nam1::l = FFFFFFFFFFFFFFFF Nam1::l = FF FF FF FF FF FF FF FF 名前空間内の匿名共用体メンバへの代入(unsigned int 型) Nam1::i = 44444444 Nam1::l = 44 44 44 44 FF FF FF FF Nam1::i = 44 44 44 44 グローバル名前空間内の匿名共用体メンバへの代入(unsigned long 型) l = FFFFFFFFFFFFFFFF l = FF FF FF FF FF FF FF FF グローバル名前空間内の匿名共用体メンバへの代入(unsigned int 型) i = 44444444 l = 44 44 44 44 FF FF FF FF i = 44 44 44 44 関数内の匿名共用体メンバへの代入(unsigned long 型) l = FFFFFFFFFFFFFFFF l = FF FF FF FF FF FF FF FF 関数内の匿名共用体メンバへの代入(unsigned int 型) i = 44444444 l = 44 44 44 44 FF FF FF FF i = 44 44 44 44

ローカルクラス


sample.cpp
#include <iostream> // グローバル名前空間内のクラス class Smpl { int n; public: Smpl(int x) : n(x * 10) {} int getval() { return n; } }; int f(int x) { // 関数内のローカルクラス class Smpl { int n; public: Smpl(int x) : n(x * 2) {} int getval() { return n; } }; // ローカルクラスオブジェクトの生成 Smpl s(x); return s.getval(); } int main() { // グローバル名前空間内のクラスオブジェクトの生成 Smpl s(1); std::cout << "s.getval() = " << s.getval() << std::endl; std::cout << "f(1) = " << f(1) << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out s.getval() = 10 f(1) = 2

クラスメンバのアクセス制御


sample.cpp
#include <iostream> // キーワード「class」によるクラス定義 class Smpl_class { // デフォルトのアクセス制御は private // クラス内からは使用可能 int n; public: Smpl_class() : n(1) {} int getn() { return n; } protected: // 派生クラスからは使用可能 int getn2() { return n * 2; } }; // キーワード「struct」によるクラス定義 struct Smpl_struct { // デフォルトのアクセス制御は public // どこからでも使用可能 Smpl_struct() : n(0x100) {} int getn() { return n; } private: int n; }; // キーワード「union」による共用体 union Smpl_union { // デフォルトのアクセス制御は public // どこからでも使用可能 Smpl_union() : n(0x10000) {} int getn() { return n; } private: int n; }; // Smpl_classを基底クラスとする派生クラス struct Smpl_derived : public Smpl_class { // 基底クラス内のアクセス制御がprotectedであるgetn2()を使用 void getn3() { std::cout << "getn2() = " << getn2() << std::endl; } }; int main() { Smpl_class sc; Smpl_struct ss; Smpl_union su; Smpl_derived sd; std::cout << std::uppercase << std::hex; std::cout << "キーワード「class」によるクラス内のアクセス指定子が「public」である関数" << std::endl; std::cout << "sc.getn() = " << sc.getn() << std::endl; std::cout << std::endl; std::cout << "キーワード「struct」によるクラス内のアクセス指定子の無い関数" << std::endl; std::cout << "ss.getn() = " << ss.getn() << std::endl; std::cout << std::endl; std::cout << "キーワード「union」による共用体内のアクセス指定子の無い関数" << std::endl; std::cout << "su.getn() = " << su.getn() << std::endl; std::cout << std::endl; std::cout << "基底クラス内のアクセス指定子が「protected」である関数を実行して出力する派生クラス内の関数" << std::endl; sd.getn3(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out キーワード「class」によるクラス内のアクセス指定子が「public」である関数 sc.getn() = 1 キーワード「struct」によるクラス内のアクセス指定子の無い関数 ss.getn() = 100 キーワード「union」による共用体内のアクセス指定子の無い関数 su.getn() = 10000 基底クラス内のアクセス指定子が「protected」である関数を実行して出力する派生クラス内の関数 getn2() = 2

派生クラスと基底クラスに関するアクセス制御


sample.cpp
#include <iostream> class Smpl_class { unsigned int n_pri; public: unsigned int n_pub; Smpl_class() : n_pri(1), n_pub(2), n_pro(3) {} protected: unsigned int n_pro; }; // 基底クラスのアクセス指定子をpublicとして宣言 struct Derived_pub : public Smpl_class { void viewn() { std::cout << " 基底クラスのアクセス指定子をpublicとして宣言" << std::endl; std::cout << " 基底クラス内「private」以外はアクセス可能" << std::endl; std::cout << " n_pub = " << n_pub << std::endl; std::cout << " n_pro = " << n_pro << std::endl; std::cout << std::endl; } }; // 基底クラスのアクセス指定子をprivateとして宣言 struct Derived_pri : private Smpl_class { void viewn() { std::cout << " 基底クラスのアクセス指定子をprivateとして宣言" << std::endl; std::cout << " 基底クラス内「private」以外はアクセス可能" << std::endl; std::cout << " n_pub = " << n_pub << std::endl; std::cout << " n_pro = " << n_pro << std::endl; std::cout << std::endl; } }; // 基底クラスのアクセス指定子をprotectedとして宣言 struct Derived_pro : protected Smpl_class { void viewn() { std::cout << " 基底クラスのアクセス指定子をprotectedとして宣言" << std::endl; std::cout << " 基底クラス内「private」以外はアクセス可能" << std::endl; std::cout << " n_pub = " << n_pub << std::endl; std::cout << " n_pro = " << n_pro << std::endl; std::cout << std::endl; } }; int main() { Derived_pub d1; Derived_pri d2; Derived_pro d3; std::cout << "クラスオブジェクトの関数を実行して基底クラスのメンバを表示" << std::endl; d1.viewn(); d2.viewn(); d3.viewn(); std::cout << "クラスオブジェクトを介して基底クラスのメンバにアクセス" << std::endl; std::cout << " 基底クラスのアクセス指定子をpublicとして宣言" << std::endl; std::cout << " d1.n_pub = " << d1.n_pub << std::endl; std::cout << std::endl; std::cout << "クラスオブジェクトを介して基底クラスのメンバにアクセス" << std::endl; std::cout << " 基底クラスのアクセス指定子をprivateとして宣言" << std::endl; std::cout << " アクセス可能なメンバ無し" << std::endl; std::cout << std::endl; std::cout << "クラスオブジェクトを介して基底クラスのメンバにアクセス" << std::endl; std::cout << " 基底クラスのアクセス指定子をprivateとして宣言" << std::endl; std::cout << " アクセス可能なメンバ無し" << std::endl; std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out クラスオブジェクトの関数を実行して基底クラスのメンバを表示 基底クラスのアクセス指定子をpublicとして宣言 基底クラス内「private」以外はアクセス可能 n_pub = 2 n_pro = 3 基底クラスのアクセス指定子をprivateとして宣言 基底クラス内「private」以外はアクセス可能 n_pub = 2 n_pro = 3 基底クラスのアクセス指定子をprotectedとして宣言 基底クラス内「private」以外はアクセス可能 n_pub = 2 n_pro = 3 クラスオブジェクトを介して基底クラスのメンバにアクセス 基底クラスのアクセス指定子をpublicとして宣言 d1.n_pub = 2 クラスオブジェクトを介して基底クラスのメンバにアクセス 基底クラスのアクセス指定子をprivateとして宣言 アクセス可能なメンバ無し クラスオブジェクトを介して基底クラスのメンバにアクセス 基底クラスのアクセス指定子をprivateとして宣言 アクセス可能なメンバ無し

フレンド(friend)


sample.cpp
#include <iostream> class Smpl1 { int n; // 関数をフレンドとして宣言 friend int getn(Smpl1 *ps); // クラスをフレンドとして宣言 friend class Smpl2; public: Smpl1() : n(1) {} }; // フレンドとして宣言されたクラス struct Smpl2 { int n2; Smpl2() : n2(2) {} // フレンドクラス内でSmpl1内のprivate指定されたメンバを使用 int getn2(Smpl1& rs) { return rs.n + n2; } }; // フレンドとして宣言された関数 // Smpl1内のprivate指定されたメンバを使用 int getn(Smpl1 *ps) { return ps->n; } int main() { Smpl1 s1; Smpl2 s2; std::cout << "getn(&s1) = " << getn(&s1) << std::endl; std::cout << "s2.getn2(s1) = " << s2.getn2(s1) << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out getn(&s1) = 1 s2.getn2(s1) = 3

コンストラクタ


sample.cpp
#include <iostream> class Smpl { int n1; int n2; int n3; public: // 初期化子リストを使用した引数無しのコンストラクタ Smpl() : n1(1), n2(2), n3(3) {} // 引数から初期化子リストで初期化するコンストラクタ Smpl(int i, int j, int k) : n1(i), n2(j), n3(k) {} // 引数から初期化子リストで初期化するコンストラクタ Smpl(int i, int j = 8) : n1(i), n2(j), n3(j + 1) {} void view() { std::cout << "n1 = " << n1 << " --- n2 = " << n2 << " --- n3 = " << n3 << std::endl; } }; int main() { std::cout << "引数無しでインスタンスを宣言" << std::endl; Smpl s1; s1.view(); std::cout << std::endl; std::cout << "引数3つでインスタンスを宣言" << std::endl; Smpl s2(4, 5, 6); s2.view(); std::cout << std::endl; std::cout << "引数1つだけでインスタンスを宣言" << std::endl; Smpl s3(7); s3.view(); std::cout << std::endl; std::cout << "引数1つだけでインスタンスを宣言" << std::endl; Smpl s4(10, 11); s4.view(); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 引数無しでインスタンスを宣言 n1 = 1 --- n2 = 2 --- n3 = 3 引数3つでインスタンスを宣言 n1 = 4 --- n2 = 5 --- n3 = 6 引数1つだけでインスタンスを宣言 n1 = 7 --- n2 = 8 --- n3 = 9 <--- 足りない引数には既定値を使用 引数1つだけでインスタンスを宣言 n1 = 10 --- n2 = 11 --- n3 = 12

デフォルトコンストラクタ


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } class Smpl1 { // メンバの初期化 int n1{1}; int n2{2}; public: // コンストラクタの記述無し void view() { std::cout << n1 << ", " << n2 << std::endl; } }; class Smpl2 { int n1; int n2; public: // 既定値を持つ引数を伴うデフォルトコンストラクタ Smpl2(int i1 = 11, int i2 = 12) : n1(i1), n2(i2) {} void view() { std::cout << n1 << ", " << n2 << std::endl; } }; int main() { // デフォルトコンストラクタ(暗黙的)の呼び出し Smpl1 s1; // デフォルトコンストラクタの呼び出し Smpl2 s3; // 引数を伴うコンストラクタの呼び出し Smpl2 s2(3, 4); // 引数を伴うコンストラクタの呼び出し(初期化子リスト) Smpl2 s4{5, 6}; std::cout << std::endl; s1.view(); s2.view(); s3.view(); s4.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); byteseq(reinterpret_cast<unsigned char *>(&s4), sizeof(s4), "s4"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 1, 2 3, 4 11, 12 5, 6 s1 = 01 00 00 00 02 00 00 00 s2 = 03 00 00 00 04 00 00 00 s3 = 0B 00 00 00 0C 00 00 00 s4 = 05 00 00 00 06 00 00 00

コピーコンストラクタ(暗黙的)


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl1 { // メンバの初期化 int n1{1}; int n2{2}; // コンストラクタの記述無し void view() { std::cout << "n1 = " << n1 << ", n2 = " << n2 << std::endl; } }; struct Smpl2 { // メンバの初期化 int n{1}; double d{11.22}; unsigned short u[3]{3, 4, 5}; // 配列型メンバを含む // コンストラクタの記述無し void view() { std::cout << "n = " << n << ", d = " << d << ", u = " << u << std::endl; } }; int main() { // デフォルトコンストラクタ(既定値)による Smpl1 クラスオブジェクトの生成 Smpl1 s1; // コピーコンストラクタ(暗黙的)の呼び出し Smpl1 s2(s1); // デフォルトコンストラクタ(既定値)による Smpl2 クラスオブジェクトの生成 Smpl2 s3; // コピーコンストラクタ(暗黙的)の呼び出し Smpl2 s4(s3); // 初期化リストによる Smpl2 クラスオブジェクトの生成 Smpl2 s5{11, 2.345, {6, 7, 8}}; // コピーコンストラクタ(暗黙的)の呼び出し Smpl2 s6(s5); std::cout << std::endl; s1.view(); s2.view(); s3.view(); s4.view(); s5.view(); s6.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); byteseq(reinterpret_cast<unsigned char *>(&s4), sizeof(s4), "s4"); byteseq(reinterpret_cast<unsigned char *>(&s5), sizeof(s5), "s5"); byteseq(reinterpret_cast<unsigned char *>(&s6), sizeof(s6), "s6"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out n1 = 1, n2 = 2 n1 = 1, n2 = 2 n = 1, d = 11.22, u = 0x7fff14df1850 n = 1, d = 11.22, u = 0x7fff14df1830 <--- 配列のアドレスはコピーに際して新規に確保 n = 11, d = 2.345, u = 0x7fff14df1810 n = 11, d = 2.345, u = 0x7fff14df17f0 <--- 配列のアドレスはコピーに際して新規に確保 s1 = 01 00 00 00 02 00 00 00 s2 = 01 00 00 00 02 00 00 00 s3 = 01 00 00 00 00 00 00 00 71 3D 0A D7 A3 70 26 40 03 00 04 00 05 00 00 00 s4 = 01 00 00 00 00 00 00 00 71 3D 0A D7 A3 70 26 40 03 00 04 00 05 00 00 00 <--- メンバの値は全てコピー s5 = 0B 00 00 00 00 00 00 00 C3 F5 28 5C 8F C2 02 40 06 00 07 00 08 00 00 00 s6 = 0B 00 00 00 00 00 00 00 C3 F5 28 5C 8F C2 02 40 06 00 07 00 08 00 00 00 <--- メンバの値は全てコピー

暗黙的および明示的コピーコンストラクタの比較テスト


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl1 { // メンバの初期化 int n{1}; double d{11.22}; unsigned short u[3]{3, 4, 5}; // 配列型メンバを含む // コンストラクタの記述無し void view() { std::cout << "Smpl1 : n = " << n << ", d = " << d << ", u = " << u << std::endl; } }; struct Smpl2 { // メンバの初期化 int n{2}; double d{33.44}; unsigned short u[3]{5, 6, 7}; // 配列型メンバを含む // デフォルトコンストラクタの定義(コピーコンストラクタ記述時必須) Smpl2() {} // 暗黙的なものと同様の機能となるようなコピーコンストラクタを記述 Smpl2(const Smpl2& rs) : n(rs.n), d(rs.d) { for (int i=0; i<3; i++) { u[i] = rs.u[i]; } } void view() { std::cout << "Smpl2 : n = " << n << ", d = " << d << ", u = " << u << std::endl; } }; int main() { // Smpl1 クラスオブジェクトの生成 Smpl1 s1; // コピーコンストラクタ(暗黙的)の呼び出し Smpl1 s2(s1); // Smpl2 クラスオブジェクトの生成 Smpl2 s3; // オブジェクトのメンバをデフォルト値から変更 s3.n = 3; s3.d = 44.55; s3.u[0] = 6; s3.u[1] = 7; s3.u[2] = 8; // コピーコンストラクタの呼び出し Smpl2 s4(s3); std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); std::cout << "s3 --- "; s3.view(); std::cout << "s4 --- "; s4.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); byteseq(reinterpret_cast<unsigned char *>(&s4), sizeof(s4), "s4"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out s1 --- Smpl1 : n = 1, d = 11.22, u = 0x7ffe6e571590 s2 --- Smpl1 : n = 1, d = 11.22, u = 0x7ffe6e571570 <--- 暗黙的コピーコンストラクタによるコピー s3 --- Smpl2 : n = 3, d = 44.55, u = 0x7ffe6e571550 s4 --- Smpl2 : n = 3, d = 44.55, u = 0x7ffe6e571530 <--- コードを記述したコピーコンストラクタによるコピー s1 = 01 00 00 00 00 00 00 00 71 3D 0A D7 A3 70 26 40 03 00 04 00 05 00 00 00 s2 = 01 00 00 00 00 00 00 00 71 3D 0A D7 A3 70 26 40 03 00 04 00 05 00 00 00 <--- 暗黙的コピーコンストラクタによるコピー s3 = 03 00 00 00 00 00 00 00 66 66 66 66 66 46 46 40 06 00 07 00 08 00 00 00 s4 = 03 00 00 00 00 00 00 00 66 66 66 66 66 46 46 40 06 00 07 00 08 00 00 00 <--- コードを記述したコピーコンストラクタによるコピー

ヒープ領域に確保した配列へのポインタを含むクラスのコピーコンストラクタ


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { int nlen; unsigned short *pu; // コンストラクタ Smpl(int n, unsigned short *p); // デストラクタ ~Smpl() { std::cout << pu << " --- Destructor" << std::endl; delete [] pu; } // コピーコンストラクタ Smpl(const Smpl& rs); void view(); }; // コンストラクタ Smpl::Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; // ヒープ領域に確保した配列へのポインタ for(int i=0; i<nlen; i++) { pu[i] = p[i]; } } // コピーコンストラクタ Smpl::Smpl(const Smpl& rs) : nlen(rs.nlen), pu(new unsigned short[rs.nlen]) { for(int i=0; i<nlen; i++) { pu[i] = rs.pu[i]; } } void Smpl::view() { std::cout << "Smpl : nlen = " << nlen << ", pu = " << pu << std::endl; for (int i=0; i<nlen; i++) { std::cout << pu[i] << std::endl; } std::cout << std::endl; } int main() { // Smpl クラスオブジェクトの生成 unsigned short us[3]{10, 20, 30}; Smpl s1(3, us); // コピーコンストラクタ Smpl s2(s1); std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out s1 --- Smpl : nlen = 3, pu = 0x22e4eb0 10 20 30 s2 --- Smpl : nlen = 3, pu = 0x22e4ed0 10 20 30 s1 = 03 00 00 00 00 00 00 00 B0 4E 2E 02 00 00 00 00 s2 = 03 00 00 00 00 00 00 00 D0 4E 2E 02 00 00 00 00 0x22e4ed0 --- Destructor 0x22e4eb0 --- Destructor

移動コンストラクタ


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { int nlen; unsigned short *pu; // コンストラクタ Smpl(int n, unsigned short *p); // デストラクタ ~Smpl(); // 移動コンストラクタ Smpl(Smpl&& rs); void view(); }; // コンストラクタ Smpl::Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; // ヒープ領域に確保した配列へのポインタ for(int i=0; i<nlen; i++) { pu[i] = p[i]; } std::cout << "constructor" << std::endl; } // デストラクタ Smpl::~Smpl() { if (pu != nullptr) { delete [] pu; } std::cout << pu << " --- destructor" << std::endl; } // 移動コンストラクタ Smpl::Smpl(Smpl&& rs) : nlen(0), pu(nullptr) { // オブジェクトのメンバーに移動元オブジェクトのメンバ値を設定 nlen = rs.nlen; pu = rs.pu; // 移動元オブジェクトのメンバを書き換え rs.nlen = 0; rs.pu = nullptr; std::cout << "move constructor" << std::endl; } void Smpl::view() { std::cout << std::dec; std::cout << "Smpl : nlen = " << nlen << ", pu = " << pu << std::endl; for (int i=0; i<nlen; i++) { std::cout << pu[i] << std::endl; } std::cout << std::endl; } int main() { // 移動元オブジェクトの生成 unsigned short us[3]{10, 20, 30}; Smpl s1(3, us); std::cout << "s1 --- "; s1.view(); byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); std::cout << std::endl; // 移動コンストラクタの呼び出し Smpl s2(std::move(s1)); // s1を右辺値へキャストして移動コンストラクタを呼び出す std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out constructor s1 --- Smpl : nlen = 3, pu = 0x868eb0 <--- 移動元オブジェクト 10 20 30 s1 = 03 00 00 00 00 00 00 00 B0 8E 86 00 00 00 00 00 <--- 移動元オブジェクトのバイト列 move constructor <--- 移動コンストラクタの呼び出し s1 --- Smpl : nlen = 0, pu = 0 <--- 移動処理後の移動元オブジェクト s2 --- Smpl : nlen = 3, pu = 0x868eb0 <--- 移動先オブジェクト 10 20 30 s1 = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <--- 移動元オブジェクトのバイト列(移動後) s2 = 03 00 00 00 00 00 00 00 B0 8E 86 00 00 00 00 00 <--- 移動先オブジェクトのバイト列 0x868eb0 --- destructor 0 --- destructor

コピー代入演算子


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { int nlen; unsigned short *pu; // コンストラクタ Smpl(int n, unsigned short *p); // デストラクタ ~Smpl() { std::cout << pu << " --- Destructor" << std::endl; delete [] pu; } // コピーコンストラクタ Smpl(const Smpl& rs); // コピー代入演算子 Smpl& operator=(const Smpl& rs); void view(); }; // コンストラクタ Smpl::Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; // ヒープ領域に確保した配列へのポインタ for(int i=0; i<nlen; i++) { pu[i] = p[i]; } } // コピーコンストラクタ Smpl::Smpl(const Smpl& rs) : nlen(rs.nlen), pu(new unsigned short[rs.nlen]) { std::cout << "copy constructor" << std::endl; for(int i=0; i<nlen; i++) { pu[i] = rs.pu[i]; } } // コピー代入演算子 Smpl& Smpl::operator=(const Smpl& rs) { std::cout << "copy assignment operator" << std::endl; if (this != &rs) { delete [] pu; // 既存のヒープ領域上に確保されたメモリ(配列)を解放 nlen = rs.nlen; // 配列長の設定 pu = new unsigned short[nlen]; // 新規にヒープ領域に配列を確保 for(int i=0; i<nlen; i++) { pu[i] = rs.pu[i]; } } return *this; } void Smpl::view() { std::cout << std::dec; std::cout << "Smpl : nlen = " << nlen << ", pu = " << pu << std::endl; for (int i=0; i<nlen; i++) { std::cout << pu[i] << std::endl; } std::cout << std::endl; } int main() { // Smpl クラスオブジェクトの生成 unsigned short us1[3]{10, 20, 30}; unsigned short us2[2]{100, 200}; Smpl s1(3, us1); Smpl s2(2, us2); // コピーコンストラクタの呼び出し Smpl s3 = s1; std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); std::cout << "s3 --- "; s3.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; // コピー代入演算子の呼び出し s2 = s1; std::cout << "s2 --- "; s2.view(); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out copy constructor s1 --- Smpl : nlen = 3, pu = 0x969eb0 10 20 30 s2 --- Smpl : nlen = 2, pu = 0x969ed0 100 200 s3 --- Smpl : nlen = 3, pu = 0x969ef0 <--- s1からコピーコンストラクタにより生成 10 20 30 s1 = 03 00 00 00 00 00 00 00 B0 9E 96 00 00 00 00 00 s2 = 02 00 00 00 00 00 00 00 D0 9E 96 00 00 00 00 00 s3 = 03 00 00 00 00 00 00 00 F0 9E 96 00 00 00 00 00 copy assignment operator <--- s1からコピー代入演算子によりs2にコピー s2 --- Smpl : nlen = 3, pu = 0x969ed0 10 20 30 s2 = 03 00 00 00 00 00 00 00 D0 9E 96 00 00 00 00 00 <--- コピー代入演算子による演算結果 0x969ef0 --- Destructor 0x969ed0 --- Destructor 0x969eb0 --- Destructor

移動代入演算子


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { int nlen; unsigned short *pu; // コンストラクタ Smpl(int n, unsigned short *p); // デストラクタ ~Smpl(); // 移動コンストラクタ Smpl(Smpl&& rs); // 移動代入演算子 Smpl& operator=(Smpl&& rs); void view(); }; // コンストラクタ Smpl::Smpl(int n, unsigned short *p) : nlen(n) { pu = new unsigned short[nlen]; // ヒープ領域に確保した配列へのポインタ for(int i=0; i<nlen; i++) { pu[i] = p[i]; } std::cout << "constructor" << std::endl; } // デストラクタ Smpl::~Smpl() { if (pu != nullptr) { delete [] pu; } std::cout << pu << " --- destructor" << std::endl; } // 移動コンストラクタ Smpl::Smpl(Smpl&& rs) : nlen(0), pu(nullptr) { // オブジェクトのメンバーに移動元オブジェクトのメンバ値を設定 nlen = rs.nlen; pu = rs.pu; // 移動元オブジェクトのメンバを書き換え rs.nlen = 0; rs.pu = nullptr; std::cout << "move constructor" << std::endl; } // 移動代入演算子 Smpl& Smpl::operator=(Smpl&& rs) { if (this != &rs) { delete [] pu; // 既存のヒープ領域上に確保されたメモリ(配列)を解放 // オブジェクトのメンバーに移動元オブジェクトのメンバ値を設定 nlen = rs.nlen; pu = rs.pu; // 移動元オブジェクトのメンバを書き換え rs.nlen = 0; rs.pu = nullptr; } std::cout << "move assignment operator" << std::endl; return *this; } void Smpl::view() { std::cout << std::dec; std::cout << "Smpl : nlen = " << nlen << ", pu = " << pu << std::endl; for (int i=0; i<nlen; i++) { std::cout << pu[i] << std::endl; } std::cout << std::endl; } int main() { // 移動元オブジェクトの生成 unsigned short us1[3]{10, 20, 30}; unsigned short us2[2]{100, 200}; Smpl s1(3, us1); Smpl s2(2, us2); std::cout << "s1 --- "; s1.view(); byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); std::cout << std::endl; // 移動コンストラクタの呼び出し Smpl s3(std::move(s1)); // s1を右辺値へキャストして移動コンストラクタを呼び出す std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); std::cout << "s3 --- "; s3.view(); byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); byteseq(reinterpret_cast<unsigned char *>(&s3), sizeof(s3), "s3"); std::cout << std::endl; // 移動代入演算子の呼び出し s2 = std::move(s3); // s3を右辺値へキャストして移動代入演算子を呼び出す std::cout << "s2 --- "; s2.view(); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out constructor constructor s1 --- Smpl : nlen = 3, pu = 0x1138eb0 10 20 30 s1 = 03 00 00 00 00 00 00 00 B0 8E 13 01 00 00 00 00 move constructor <--- s1から移動コンストラクタによりs3に移動 s1 --- Smpl : nlen = 0, pu = 0 <--- 移動に伴い値は削除 s2 --- Smpl : nlen = 2, pu = 0x11392e0 100 200 s3 --- Smpl : nlen = 3, pu = 0x1138eb0 10 20 30 s1 = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <--- 移動に伴い値は削除 s2 = 02 00 00 00 00 00 00 00 E0 92 13 01 00 00 00 00 s3 = 03 00 00 00 00 00 00 00 B0 8E 13 01 00 00 00 00 move assignment operator <--- s3から移動代入演算子によりs2に移動 s2 --- Smpl : nlen = 3, pu = 0x1138eb0 10 20 30 s2 = 03 00 00 00 00 00 00 00 B0 8E 13 01 00 00 00 00 <--- 移動代入演算子による演算結果 0 --- destructor 0x1138eb0 --- destructor 0 --- destructor

デストラクタ


sample.cpp
#include <iostream> #include <iomanip> class Smpl { size_t nlen; char* p; public: Smpl(size_t i); // デストラクタ ~Smpl() { std::cout << p << " --- Destructor" << std::endl; // pへのメモリ割り当てを解除 delete [] p; } void byteseq(const char *str); }; Smpl::Smpl(size_t i) : nlen(i) { p = new char[i]; std::cout << "p(address) = " << static_cast<void *>(p) << std::endl; for (size_t j=0; j<(i-1); j++) { p[j] = 'A' + j; } std::cout << "p = " << p << std::endl; } void Smpl::byteseq(const char *str) { std::cout << std::uppercase << std::hex; std::cout << str << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(p[i]) << " "; } std::cout << std::endl; } int main() { Smpl s(5); Smpl *p = new Smpl(10); std::cout << "&s = " << &s << std::endl; std::cout << "p = " << p << std::endl; s.byteseq("s"); p->byteseq("p"); // new演算子でのクラスオブジェクトへのメモリ割り当てを解除 delete p; std::cout << "delete p" << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out p(address) = 0x1fe2eb0 p = ABCD p(address) = 0x1fe3300 p = ABCDEFGHI &s = 0x7ffca5201a90 p = 0x1fe32e0 s = 41 42 43 44 00 p = 41 42 43 44 45 46 47 48 49 00 ABCDEFGHI --- Destructor <--- delete演算子によるデストラクタ呼び出し delete p ABCD --- Destructor <--- 実行終了に伴うデストラクタ呼び出し

暗黙的コピーコンストラクタ使用時の明示的デストラクタの挙動


sample.cpp
#include <iostream> #include <iomanip> // バイト列表示用関数 void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname) { std::cout << std::uppercase << std::hex; std::cout << tname << " = "; for (std::size_t i=0; i<nlen; i++) { std::cout << std::setw(2) << std::setfill('0') << static_cast<int>(cp[i]) << " "; } std::cout << std::endl; } struct Smpl { int nlen; unsigned short *pu; Smpl(int n); // デストラクタを明示的に定義 ~Smpl() { std::cout << pu << " --- Destructor" << std::endl; delete [] pu; } void view() { std::cout << "Smpl : nlen = " << nlen << ", pu = " << pu << std::endl; } }; Smpl::Smpl(int n) : nlen(n) { pu = new unsigned short[nlen]; // new演算子によりヒープ領域に配列を確保 } int main() { // Smpl クラスオブジェクトの生成 Smpl s1(2); // メンバの値をデフォルト値から変更 s1.pu[0] = 10; s1.pu[1] = 20; // コピーコンストラクタ(暗黙的)の呼び出し Smpl s2(s1); std::cout << std::endl; std::cout << "s1 --- "; s1.view(); std::cout << "s2 --- "; s2.view(); std::cout << std::endl; byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1"); byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2"); std::cout << std::endl; }

実行結果
$ gcc -Wall sample1.cpp -lstdc++ $ ./a.out s1 --- Smpl : nlen = 2, pu = 0x14faeb0 s2 --- Smpl : nlen = 2, pu = 0x14faeb0 <--- 配列を指しているアドレスがコピーされる s1 = 02 00 00 00 00 00 00 00 B0 AE 4F 01 00 00 00 00 s2 = 02 00 00 00 00 00 00 00 B0 AE 4F 01 00 00 00 00 0x14faeb0 --- Destructor <--- 実行終了時のデストラクタ呼び出し 0x14faeb0 --- Destructor free(): double free detected in tcache 2 <--- 同じ記憶領域を2回解放しようとしてエラー発生 Aborted (core dumped)

コンストラクタにおける真偽を結果とする演算子によるクラスメンバの初期化


sample.cpp
#include <iostream> class Smpl { int n1; int n2; int n3; public: // 真偽を結果とする演算子によるメンバの初期化 Smpl(int *p1, int *p2, int *p3) : n1(p1 == NULL), // 等価演算子 == 真:1 偽:0 n2(p2 == NULL), n3(p3 != NULL) // 不等価演算子 != 真:1 偽:0 {} void view() { std::cout << "n1(p1 == NULL) = " << n1 << std::endl << "n2(p2 == NULL) = " << n2 << std::endl << "n3(p3 != NULL) = " << n3 << std::endl; } }; int main() { int i1 = 1; int i3 = 3; int *pi1 = &i1; int *pi2 = nullptr; int *pi3 = &i3; Smpl s1(pi1, pi2, pi3); s1.view(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out n1(p1 == NULL) = 0 n2(p2 == NULL) = 1 n3(p3 != NULL) = 1

実行環境

GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39


コード例・出力内容中の表記

・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。