アフィリエイト広告を利用しています

広告

この広告は30日以上更新がないブログに表示されております。
新規記事の投稿を行うことで、非表示にすることが可能です。
posted by fanblog

2017年12月07日

《その167》 静的メンバへのポインタ & p.135演習3-14


 静的メンバへのポインタ

 静的データメンバへのポインタ や 静的メンバ関数へのポインタについて、チェックしてみたのが次のプログラムです。
 クラスの静的メンバを指すポインタは、通常のタイプのポインタ型として定義します。静的メンバは、クラスに属してはいますが、その実体は個々のクラスオブジェクトとは無関係なので、通常のタイプのポインタで指すことができます。

// ------------------------------------
#include <iostream>
using namespace std;

class C {
public:
static int n; // 静的データメンバ
static void func() { // 静的メンバ関数
std::cout << "static void func();\n";
}
int a; // 通常のデータメンバ
int get_a() const{ return a; }
// 通常のメンバ関数
C() { a = 1000; } // コンストラクタ
};

int C::n = 9999;
// 静的データメンバの実体は、クラス定義の外で
// static を付けずに定義する。


int main()
{
C test;

int *ptr1 = &C::n;
// 静的データメンバへのポインタ
void(*ptr2)() = &C::func;
// 静的メンバ関数へのポインタ
int C::*ptr3 = &C::a;
// 通常のデータメンバへのポインタ
int (C::*ptr4)() const= &C::get_a;
// 通常の静的メンバ関数へのポインタ

cout << *ptr1 << '\n';
(*ptr2)();
cout << test.*ptr3 << '\n';
cout << (test.*ptr4)() << '\n';
}
// ------------------------------------
f03_0039.png


新版明解C++中級編 p.135 演習3-14
 次のようなクラス Person と、Person型の配列がある。この配列をクイックソートアルゴリズムによってソートするプログラムを作成せよ。qsort関数を利用することなく実現すること。
【 クラス person 】
class Person {
public:
char name[10]; // 名前
int height; // 身長
int weight; // 体重
};

【 Person型の配列 】
Person x[]= {{"Shibata", 170, 52},
{"Takaoka", 180, 70},
{"Nangoh", 172, 63},
{"Sugiyama", 165, 50},
};

// p135_演習3-14
#include <iomanip>
#include <cstdlib>
#include <iostream>
using namespace std;

class Person {
public:
char name[10]; // 名前
int height; // 身長
int weight; // 体重
};

// x, yの指すnバイトの領域を交換する関数。
namespace {
void memswap(void* x, void* y, size_t n)
{
unsigned char* a = reinterpret_cast<unsigned char*>(x);
unsigned char* b = reinterpret_cast<unsigned char*>(y);

for (; n--; a++, b++) {
unsigned char c = *a;
*a = *b;
*b = c;
}
}
}

void quicksort(void* base, size_t nmemb, size_t size,
int(*compar)(const void*, const void*))
{
if (nmemb > 0) {
const char* v = reinterpret_cast<const char*>(base);
// 配列 base の先頭要素へのポインタ v
size_t pl = 0; // 探索範囲左端の位置(添字)
size_t pr = nmemb - 1; // 探索範囲右端の位置(添字)
size_t pv = nmemb; // 基準値の位置(添字)
size_t pt = (pl + pr) / 2; // 更新後の基準値の位置(添字)

do {
const char* x = &v[pt * size];
// const char*型のポインタ x
// 関数内の配列 v の各要素を指すポインタ用の変数です。
// v[pt * size] は 基準値 base[pt] に対応します。


if (pv != pt) x = &v[(pv = pt) * size];
// 基準値の位置に変更があった場合は、更新します。

while (compar(reinterpret_cast<const void*>(&v[pl * size]), x) < 0)
pl++;
// 探索範囲左端からチェックしていき、基準値のほうが大きければ、
// 探索範囲左端の位置 pl をインクリメント。
// 基準値以上の値を見つけたら、pl はその値の位置で止まります。


while (compar(reinterpret_cast<const void*>(&v[pr * size]), x) > 0)
pr--;
// 探索範囲右端からチェックしていき、基準値のほうが小さければ、
// 探索範囲右端の位置 pr をデクリメント。
// 基準値以下の値を見つけたら、pr はその値の位置で止まります。


if (pl <= pr) {
// 探索範囲の左端と右端が逆転していないうちは・・・
pt = (pl == pv) ? pr : (pr == pv) ? pl : pv;
// 探索範囲の左端が基準値の位置に達している場合は、基準値の位置
// を現時点での右端の位置に更新します。
// 探索範囲の右端が基準値の位置に達している場合は、基準値の位置
// を現時点での左端の位置に更新します。
// それ以外なら、基準値の位置は更新しません。
// ※以上の操作は、memswap関数が値を交換しても基準値の値が変化
// しないようにするため。


memswap(const_cast<void*>(reinterpret_cast<const void*>(&v[pl * size])),
const_cast<void*>(reinterpret_cast<const void*>(&v[pr * size])),
size);
pl++;
if (pr == 0) // 符号無し整数 0 からのデクリメントを避ける。
goto QuickRight;
pr--;
}
} while (pl <= pr);
// 配列 base は、do文が終了すると、quicksort関数内で最初に決めた
// 基準値 base[pt] に対してその値以上の部分と以下の部分に分けられます。


if (0 < pr)
// do文で二つに分けられた区間の左側を対象にして、
// quicksort関数を再帰的に呼び出します。

quicksort(const_cast<void*>(reinterpret_cast<const void*>(&v[0])),
pr + 1, size, compar);
QuickRight:
if (pl < nmemb - 1)
// do文で二つに分けられた区間の右側を対象にして、
// quicksort関数を再帰的に呼び出します。

quicksort(const_cast<void*>(reinterpret_cast<const void*>(&v[pl * size])),
nmemb - pl, size, compar);
}
}

int acmp(char* x, char* y) {
// 比較関数 : 配列による文字列用
return strcmp(x, y);
}

int pcmp(char** x, char** y) {
// 比較関数 : ポインタによる文字列用
return strcmp(*x, *y);
}

int ncmp(int* x, int* y) {
// 比較関数 : int型の整数用
return *x < *y ? -1 :
*x > *y ? 1 : 0;
}

int npcmp(const Person* x, const Person* y) {
// 比較関数 : Person型の名前メンバ用
return strcmp((*x).name, (*y).name);
}

int hpcmp(const Person* x, const Person* y) {
// 比較関数 : Person型の身長メンバ用
return (*x).height < (*y).height ? -1 :
(*x).height >(*y).height ? 1 : 0;
}

int wpcmp(const Person* x, const Person* y) {
// 比較関数 : Person型の体重メンバ用
return (*x).weight < (*y).weight ? 1 :
(*x).weight >(*y).weight ? -1 : 0;
}

void print_person(const Person x[], int no) {
// Person型データを一覧表示
for (int i = 0; i < no; i++)
cout << setw(10) << left << x[i].name << " "
<< x[i].height << "cm " << x[i].weight << "kg\n";
}


int main()
{
Person x[] = { { "Shibata", 170, 52 },
{ "Takaoka", 180, 70 },
{ "Nangoh", 172, 63 },
{ "Sugiyama", 165, 50 },
};

int nx = sizeof(x) / sizeof(x[0]);

puts("ソート前");
print_person(x, nx);

// 名前昇順にソート
quicksort(x, nx, sizeof(Person),
reinterpret_cast<int(*)(const void*, const void*)>(npcmp));

cout << "\n名前昇順ソート後\n";
print_person(x, nx);

// 身長昇順にソート
quicksort(x, nx, sizeof(Person),
reinterpret_cast<int(*)(const void*, const void*)>(hpcmp));

cout << "\n身長昇順ソート後\n";
print_person(x, nx);

// 体重降順にソート
quicksort(x, nx, sizeof(Person),
reinterpret_cast<int(*)(const void*, const void*)>(wpcmp));

cout << "\n体重降順ソート後\n";
print_person(x, nx);
}

f03_14.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その166》 データメンバへのポインタ & p.133演習3-13


 クラスのデータメンバへのポインタ

 データメンバへのポインタは、メンバ関数のときと同じ形式で宣言します。
次のプログラムで確認してください。

// ------------------------------------
#include <iostream>

class C {
public:
char chr;
C() { chr = 'a'; }
char ch() { return chr; }
};

C c;// C型のクラスオブジェクト c を作る。

int main() {
char C::*ptr_c; // データメンバへのポインタ ptr_c の宣言
char (C::*ptr_f)(); // メンバ関数へのポインタ ptr_f の宣言

ptr_c = &C::chr;
// ポインタ ptr_c がデータメンバ C::chr を指すように設定
ptr_f = &C::ch;
// ポインタ ptr_f がメンバ関数 C::ch を指すように設定

std::cout << (c.*ptr_f)() << '\n';
// std::cout << c.ch() << '\n'; と同じこと
c.*ptr_c = 'b';
// c.chr = 'b'; と同じこと
std::cout << (c.*ptr_f)() << '\n';
}
// ------------------------------------

f03_0038.png


新版明解C++中級編 p.133 演習3-13
 次のプログラムは、ユーザが年/月/日の中から選んだ値を当てさせる日付当てゲームである。
年/月/日のすべてを、この順で当てさせるようにしたプログラムを作成せよ。なお、すべての値が当たるまでは、プログラムは終了しないものとする。

// ------------------------------------
// SimpleDate.h ヘッダファイル

#ifndef ___Class_SimpleDate
#define ___Class_SimpleDate

class SimpleDate {
int y; // 西暦年
int m; // 月
int d; // 日

public:
SimpleDate(int yy = 1, int mm = 1, int dd = 1)
: y(yy), m(mm), d(dd) { }

int year() const { return y; }
int month() const { return m; }
int day() const { return d; }

int comp_y(int yy) const { return yy - y; }
// yyから年を減じた値を返却
int comp_m(int mm) const { return mm - m; }
// mmから月を減じた値を返却
int comp_d(int dd) const { return dd - d; }
// ddから日を減じた値を返却
};

#endif

// ------------

// SimpleDateTest.cpp ファイル

#include <iostream>
#include "SimpleDate.h"
using namespace std;

int main() {
typedef int (SimpleDate::*Comp)(int) const;
Comp pcomp[] = {
&SimpleDate::comp_y,
&SimpleDate::comp_m,
&SimpleDate::comp_d,
};
int menu;
const SimpleDate birthday(1963, 11, 18);

cout << "私の誕生日を当ててください。\n";

do {
cout << "0…年/1…月/2…日/3…終了:"; cin >> menu;

if (menu >= 0 && menu <= 2) {
int value;
cout << "いくつかな:"; cin >> value;

int diff = (birthday.*pcomp[menu])(value);
if (!diff)
cout << "正解です。\n";
else if (diff > 0)
cout << diff << "だけ大きいです。\n";
else
cout << -diff << "だけ小さいです。\n";
}
} while (menu != 3);
}
// ------------------------------------

 下のプログラムが解答です。なお、ヘッダファイル SimpleDate.h は問題で指定された上記のプログラムと同じなので、SimpleDateTest.cppファイルのコードだけを下記します。

// p133_演習3-13
// 問題で指定された仕様に変更した SimpleDateTest.cppプログラム
#include <iostream>
#include "SimpleDate.h"
using namespace std;

int main() {
typedef int (SimpleDate::*Comp)(int) const;
Comp pcomp[] = {
&SimpleDate::comp_y,
&SimpleDate::comp_m,
&SimpleDate::comp_d,
};
const char* menu[] = { "年", "月", "日" };
const SimpleDate birthday(1963, 11, 18);

cout << "私の誕生日を当ててください。\n";

for (int i = 0; i < 3; i++) {
int value;
do {
cout << "◆ " << menu[i] << " を入力 : "; cin >> value;
int diff = (birthday.*pcomp[i])(value);

if (!diff) {
cout << "正解です。\n";
break;
}
else if (diff > 0)
cout << diff << "だけ大きいです。\n";
else
cout << -diff << "だけ小さいです。\n";
} while (1);
}
}

f03_13.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)





《その165》 関数へのポインタ,メンバ関数へのポインタ


 関数へのポインタ と メンバ関数へのポインタ

 下のプログラムに実用的な意味はありませんが、main関数が関数 func を呼び出す際に、次の情報を渡しています。
 ・呼び出された関数 func が、次に呼び出すべき関数
              (この場合は関数 f1 と f2 のどちらか)
 ・孫呼出しされる関数(f1 または f2)が使用する、クラス C のメンバ関数
              (この場合は関数 get_n1 と get_n2 のどちらか)


// ------------------------------------
#include <iostream>
using namespace std;

class C {
int num1;
int num2;
public:
C() { num1 = 100; num2 = 200; }
int get_n1() const { return num1; }
int get_n2() const { return num2; }
};

C c;

int f1(int (C::*get)() const);
int f2(int (C::*get)() const);
int func(int(*f)(int (C::*)() const), int (C::*get)() const);

int main() {
cout << func(f1, &C::get_n1) << '\n'; // @
// func(f1, &C::get_n1) … 関数 func を呼び出します。
// 引数 f1 は関数 f1 へのポインタです。関数名は、その関数へのポインタと解釈されます。
// 引数 &C::get_n1 は、クラス C のメンバ関数 get_n1 へのポインタです。

cout << func(f1, &C::get_n2) << '\n';
cout << func(f2, &C::get_n1) << '\n';
cout << func(f2, &C::get_n2) << '\n';
}

int func(int(*f)(int (C::*)() const), int (C::*get)() const) {
// @で呼び出された場合は・・・
// 仮引数の f が、ポインタ f1 を受け取ります。
// 仮引数の get が、 get_n1 へのポインタを受け取ります。


return (*f)(get);
// @で呼び出された場合は・・・
// *f は *f1 なので、関数 f1 を呼び出します。
// 引数 get は、仮引数が受け取った get です。
// 関数 f1 からの返却値を、main関数に return します。

}

int f1(int (C::*get)() const) {
// @で呼び出された関数 func から呼び出された場合は・・・
// クラス C のメンバ関数 get_n1 へのポインタを、仮引数 get が受け取ります。


return (c.*get)();
// クラスオブジェクト c のメンバ関数 get_n1 を呼び出します。
// 間接ドット演算子「 .* 」を利用しています。
// メンバ関数 get_n1 からの返却値を関数 f1 に return します。

}

int f2(int (C::*get)() const) {
return (c.*get)() * 100;
}
// ------------------------------------

f03_0037.png


新版 明解C 入門編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:13時点)

新版 明解C 中級編 (明解シリーズ)

新品価格
¥2,916から
(2017/11/10 13:14時点)






 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


検索
<< 2017年12月 >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
プロフィール
こうすけさんの画像
こうすけ

 たまに、クリック お願いします m(_ _)m

 AA にほんブログ村 IT技術ブログ C/C++へ

こうすけ:メール kousuke_cpp@outlook.jp

【1】★★C++ 記事目次★★ ← 利用可能です。
・新版明解C++入門編 / 新版明解C++中級編
・その他 C++ 関連記事

【2】★★こうすけ@C#★★
・C# の初歩的な記事


×

この広告は30日以上新しい記事の更新がないブログに表示されております。