速習 CプログラマのためのC++プログラミング入門 サンプルプログラム集

コンピュータを学習する人の学校:パソコンキャンパス、プログラミングキャンパス

ここでは、 The Office Uchida, School of Computerの e-Learningシステム 「速習 CプログラマのためのC++プログラミング入門」のためのサンプルプログラムを紹介しています。

この中に記載されているプログラムをブラウズあるいはカットアンドペーストしてください。

目次に戻る


第4章 クラス関連の話題

内容
Sample list 4-1 private変数にアクセスしてエラーとなる例
Sample list 4-2 friendの使用例
Sample list 4-3 フレンド関数
Sample list 4-4 複数のクラスにまたがるフレンド関数
Sample list 4-5 多重継承
Sample list 4-6 多重継承におけるあいまいさ
Sample list 4-6ok スコープ演算子でメンバ変数を特定
Sample list 4-7 仮想基本クラス
Sample list 4-7err 仮想基本クラス(エラー版)
Sample list 4-7ext ::演算子で特定
Sample list 4-8 仮想関数におけるメンバ関数の呼び出し
Sample list 4-9 virtual
Sample list 4-10 private継承
Sample list 4-11 グローバルスコープ(解決)演算子
Sample list 4-12 例外処理
Sample list 4-13 例外処理(型でスロー)
Sample list 4-14 クラスによる例外処理

本章では、個々のプログラムごとにファイルに分けるとプログラム数が多くなってしまうので、極力プログラムを1つのファイルに まとめて記述いたします。


friend

次のプログラムは、ClassBの定義領域の中で、ClassAの private変数にアクセスしています(赤い部分)が、これが不正になります。

Sample list 4-1 private変数にアクセスしてエラーとなる例friend_test_err.cpp
#include <iostream>
using namespace std;

class ClassA
{
private:
	int prVar; // ClassA以外はアクセスできない
public:
	int pubVar; // すべてのクラスからアクセスできる
};

class ClassB
{
public:
	void method(); // ClassBのメンバ関数
};

void ClassB::method() // ClassBのメンバ関数の定義(この部分はClassBの領域)
{
	ClassA a;

	a.prVar = 10;  // ×:ClassBの領域でClassAのprivateなメンバ変数にアクセス
	a.pubVar = 20; // ○:この変数はpublicなメンバ変数
}

Sample list 4-1をコンパイルすると、次のようにエラーメッセージが表示されます(黄色い部分)。

Sample list 4-1のコンパイル(cygwin)

% c++ -c friend_test.cpp
friend_test.cpp: In member function `void ClassB::method()':
friend_test.cpp:7: error: `int ClassA::prVar' is private
friend_test.cpp:22: error: within this context

そこで、friendの登場です。friendで、例外的にそのクラスのprivate変数にアクセスできるクラスを指定できます。

Sample list 4-2 friendの使用例friend_test.cpp
#include <iostream>
using namespace std;

class ClassA
{
friend class ClassB; // ClassBは、ClassAのprivate変数にアクセスできることを宣言
private:
	int prVar; // ClassA、ClassB以外はアクセスできない
public:
	int pubVar; // すべてのクラスからアクセスできる
};

class ClassB
{
public:
	void method(); // ClassBのメンバ関数
};

void ClassB::method() // ClassBのメンバ関数の定義(この部分はClassBの領域)
{
	ClassA a;

	a.prVar = 10;  // ○:ClassBの領域でClassAのprivateなメンバ変数にアクセス可能
	a.pubVar = 20; // ○:この変数はpublicなメンバ変数
}

Sample list 4-1をコンパイルすると、次のようにエラーメッセージが表示されます(黄色い部分)。

Sample list 4-2のコンパイル(cygwin)

% c++ -c friend_test.cpp
%

しかし、friendは、基本的にカプセル化を破壊してしまうので、注意して使いましょう。

目次に戻る, 先頭に戻る


フレンド関数

Sample list 4-3 フレンド関数friend_func1.cpp
#include <iostream>
using namespace std;

class ClassA
{
private:
	int x; // ClassA以外はアクセスできない
public:
	void pr();
	friend void func( ClassA& ); // ClassAのprivate変数にアクセスできる一般の関数
};

void ClassA::pr()
{
	cout << "ClassA x=" << this->x << endl;
}

void func( ClassA& a ) // この関数は普通の関数
{
	a.x = 10;
}

int main()
{
	ClassA a;

	func( a );
	a.pr();
}
Sample list 4-3の実行結果(cygwin)

% c++ friend_func1.cpp
% ./a
ClassA x=10

目次に戻る, 先頭に戻る


Sample list 4-4 複数のクラスにまたがるフレンド関数friend_func2.cpp
#include <iostream>
using namespace std;

class ClassB; // ClassBがクラスであることを明示

class ClassA
{
private:
	int x; // ClassA以外はアクセスできない
public:
	void pr();
	friend void func( ClassA&, ClassB& ); // ClassA, ClassBのprivate変数にアクセスできる一般の関数
};

void ClassA::pr()
{
	cout << "ClassA x=" << this->x << endl;
}

class ClassB
{
private:
	int y; // ClassB以外はアクセスできない
public:
	void pr();
	friend void func( ClassA&, ClassB& ); // ClassA, ClassBのprivate変数にアクセスできる一般の関数
};

void ClassB::pr()
{
	cout << "ClassB y=" << this->y << endl;
}

void func( ClassA& a, ClassB& b ) // この関数は普通の関数
{
	a.x = 10;
	b.y = 20;
}

int main()
{
	ClassA a;
	ClassB b;

	func( a, b );
	a.pr();
	b.pr();
}
Sample list 4-4の実行結果(cygwin)

% c++ friend_func2.cpp
% ./a
ClassA x=10
ClassB y=20

目次に戻る, 先頭に戻る


Sample list 4-5では、ClassZは、ClassXとClassYの両方を継承しています。これを多重継承と呼びます。 多重継承は、使うのが難しいことが経験上、分かっていますので、使わないようにしましょう。 ここでは、「このようなものがあるのだ」ということを説明するためにあえて紹介しています。

Sample list 4-5 多重継承multi.cpp
#include <iostream>
using namespace std;

class ClassX
{
protected:
	int x;
};

class ClassY
{
protected:
	int y;
};

class ClassZ : public ClassX, public ClassY
{
protected:
	int z;

public:
	void method()
	{
		ClassZ var;

		var.x = 1;
		var.y = 2;
		var.z = 3;
	}
};

Sample list 4-5をコンパイルしてもエラーは出ません。

Sample list 4-5のコンパイル(cygwin)

% c++ -c multi.cpp
%

次のプログラムは、継承元となるClassXClassYで メンバ変数xが重複して定義されています。これはあいまいさを引き起こすので、エラーとなってしまいます。

Sample list 4-6 多重継承におけるあいまいさmulti.cpp
#include <iostream>
using namespace std;

class ClassX
{
protected:
	int x;
};

class ClassY
{
protected:
	int y;
	int x;
};

class ClassZ : public ClassX, public ClassY
{
protected:
	int z;

public:
	void method()
	{
		ClassZ var;

		var.x = 1;
		var.y = 2;
		var.z = 3;
	}
};

Sample list 4-6をコンパイルするとつぎのエラーが出ます。

Sample list 4-6のコンパイル(cygwin)

% c++ -c multi.cpp
multi.cpp: In member function `void ClassZ::method()':
multi.cpp:27: error: request for member `x' is ambiguous
multi.cpp:14: error: candidates are: int ClassY::x
multi.cpp:7: error:                 int ClassX::x

これを避けるには、スコープ演算子でメンバ変数を特定します。次のプログラム(Sample list 4-6ok)は、正しくコンパイルできます。

Sample list 4-6ok スコープ演算子でメンバ変数を特定multiok.cpp
#include <iostream>
using namespace std;

class ClassX
{
protected:
	int x;
};

class ClassY
{
protected:
	int y;
	int x;
};

class ClassZ : public ClassX, public ClassY
{
protected:
	int z;

public:
	void method()
	{
		ClassZ var;

		var.ClassX::x = 0;
		var.ClassY::x = 1;
		var.y = 2;
		var.z = 3;
	}
};

しかし、このようなプログラムは、トラブルの元なのであまり使わないほうが良いでしょう。

目次に戻る, 先頭に戻る


仮想基本クラス

Sample list 4-7 仮想基本クラスvbc.cpp
#include<iostream>
using namespace std;

class ClassA
{
public:
	int x;
};

class ClassB : virtual public ClassA
{
public:
	int y;
};

class ClassC : virtual public ClassA
{
public:
	int z;
};

class ClassD : public ClassB, public ClassC
{
public:
	int w;
};

int main()
{
	ClassD d;

	d.x = 1;
	d.y = 2;
	d.z = 3;
	d.w = 4;

	return 0;
}

virtualがないと、...

Sample list 4-7err 仮想基本クラス(エラー版)vbc.cpp
#include<iostream>
using namespace std;

class ClassA
{
public:
	int x;
};

class ClassB : public ClassA
{
public:
	int y;
};

class ClassC : public ClassA
{
public:
	int z;
};

class ClassD : public ClassB, public ClassC
{
public:
	int w;
};

int main()
{
	ClassD d;

	d.x = 1;
	d.y = 2;
	d.z = 3;
	d.w = 4;

	return 0;
}

Sample list 4-7errをコンパイルするとつぎのエラーが出ます。

Sample list 4-7errのコンパイル(cygwin)

% c++ -c vbc.cpp
vbc.cpp: In function `int main()':
vbc.cpp:32: error: request for member `x' is ambiguous
vbc.cpp:7: error: candidates are: int ClassA::x
vbc.cpp:7: error:                 int ClassA::x

virtualを使わない場合、::演算子で特定する

Sample list 4-7ext ::演算子で特定vbc.cpp
#include<iostream>
using namespace std;

class ClassA
{
public:
	int x;
};

class ClassB : public ClassA
{
public:
	int y;
};

class ClassC : public ClassA
{
public:
	int z;
};

class ClassD : public ClassB, public ClassC
{
public:
	int w;
};

int main()
{
	ClassD d;

	d.ClassB::x = 1; // スコープ演算子でxの所属を特定する
	d.ClassC::x = 2; // スコープ演算子でxの所属を特定する
	d.y = 3;
	d.z = 4;
	d.w = 5;

	return 0;
}

目次に戻る, 先頭に戻る


仮想関数

プログラム4-8は、クラス Cのオブジェクトを指しているポインタinsapに対して、print()メンバ関数を実行しているのに、クラスAの printメンバ関数が呼ばれている例です。

Sample list 4-8 仮想関数におけるメンバ関数の呼び出しvirtual1.cpp
#include <iostream>
using namespace std;

class A {
public:
	void print();
};

class B : public A {
public:
	void print();
};

class C : public B {
public:
	void print();
};

void A::print() {
	cout << "A::print" << endl;
}

void B::print() {
	cout << "B::print" << endl;
}

void C::print() {
	cout << "C::print" << endl;
}

int main()
{
	A insa;
	B insb;
	C insc;

	insa.print();
	insb.print();
	insc.print();

	A *insap = &insc;

	insap->print();

	return 0;
}
Sample list 4-8の実行結果(cygwin)

% c++ virtual1.cpp
% ./a
A::print
B::print
C::print
A::print  <--- ポインタinsapは、クラスCを指しているのにクラスAのメンバ関数が呼ばれている

このような場合、virtualを指定してprintメンバ関数を仮想関数にします。

Sample list 4-9 virtualvirtual2.cpp
#include <iostream>
using namespace std;

class A {
public:
	virtual void print();
};

class B : public A {
public:
	virtual void print();
};

class C : public B {
public:
	virtual void print();
};

void A::print() {
	cout << "A::print" << endl;
}

void B::print() {
	cout << "B::print" << endl;
}

void C::print() {
	cout << "C::print" << endl;
}

int main()
{
	A insa;
	B insb;
	C insc;

	insa.print();
	insb.print();
	insc.print();

	A *insap = &insc;

	insap->print();

	return 0;
}
Sample list 4-9の実行結果(cygwin)

% c++ virtual2.cpp
% ./a
A::print
B::print
C::print
C::print  <--- ポインタinsapは、クラスCを指しているので、クラスCのメンバ関数が呼ばれている

目次に戻る, 先頭に戻る


private継承

クラスBは、クラスAをプライベート継承しているので、クラスBの外では、クラスAのパブリック変数にアクセスできない。

Sample list 4-10 private継承private.cpp
#include<iostream>
using namespace std;

class A
{
public:
	int x;
};

class B : private A
{
public:
	int y;
};

int main()
{
	A a;

	a.x = 10; // <-- OK!

	B b;

	b.x = 20; // <-- ERROR!
	b.y = 30; // <-- OK!

	return 0;
}
Sample list 4-10の実行結果(cygwin)

% c++ -c private.cpp
private.cpp: In function `int main()':
private.cpp:7: error: `int A::x' is inaccessible
private.cpp:24: error: within this context

目次に戻る, 先頭に戻る


グローバルスコープ(解決)演算子

Sample list 4-11 グローバルスコープ(解決)演算子gfunc.cpp
#include<iostream>
using namespace std;

void f( int x )
{
	cout << x << ":一般のf()関数が呼ばれた" << endl;
}

class A
{
public:
	void f( int );
	void g();
};

void A::f( int x )
{
	cout << x << ":クラスAのf()関数が呼ばれた" << endl;
}

void A::g()
{
	f( 1 );
	A::f( 2 );
	this->f( 3 );
	this->A::f( 4 );
	::f( 5 );
}

int main()
{
	A a;

	a.g();

	return 0;
}
Sample list 4-11の実行結果(cygwin)

% c++ gfunc.cpp
% ./a
1:クラスAのf()関数が呼ばれた
2:クラスAのf()関数が呼ばれた
3:クラスAのf()関数が呼ばれた
4:クラスAのf()関数が呼ばれた
5:一般のf()関数が呼ばれた

目次に戻る, 先頭に戻る


例外処理

Sample list 4-12 例外処理try_samp1.cpp
#include <iostream>
using namespace std;

int main()
{
	try
	{
		int a = 3;

		cout << "a=" << a << endl;
		if( a == 2 ) throw a;
	}
	catch( int i )
	{
		cout << "catch!" << endl;
	}

	return 0;
}
Sample list 4-12のaを2にして実行した結果(cygwin)

% c++ try_samp1.cpp
% ./a
a=2
catch!
Sample list 4-12のaを3にして実行した結果(cygwin)

% c++ try_samp1.cpp
% ./a
a=3

目次に戻る, 先頭に戻る


Sample list 4-13 例外処理(型でスロー)try_samp2.cpp
#include <iostream>
using namespace std;

int main()
{
	try
	{
		int a = 3;

		cout << "a=" << a << endl;
		if( a == 2 ) throw 5;
		if( a == 3 ) throw 1.2;
	}
	catch( int i )
	{
		cout << "catch! i=" << i << endl;
	}
	catch( double x )
	{
		cout << "catch! x=" << x << endl;
	}

	return 0;
}
Sample list 4-13のaを2にして実行した結果(cygwin)

% c++ try_samp2.cpp
% ./a
a=2
catch! i=5
Sample list 4-13のaを3にして実行した結果(cygwin)

% c++ try_samp2.cpp
% ./a
a=3
catch! x=1.2

目次に戻る, 先頭に戻る


Sample list 4-14 クラスによる例外処理try_samp3.cpp
#include <iostream>
using namespace std;

class A{};
class B{};
class C{};

int main()
{
	try
	{
		A a;
		B b;
		C c;
		int k;

		cout << "k=";
		cin >> k;

		if( k == 2 ) throw a;
		if( k == 3 ) throw b;
		if( k == 4 ) throw c;
	}
	catch( A aa )
	{
		cout << "catch A" << endl;
	}
	catch( B bb )
	{
		cout << "catch B" << endl;
	}
	catch( ... )
	{
		cout << "catch other" << endl;
	}

	return 0;
}
Sample list 4-14のkを2にして実行した結果(cygwin)

% c++ try_samp3.cpp
% ./a
k=2
catch A
Sample list 4-14のkを3にして実行した結果(cygwin)

% c++ try_samp3.cpp
% ./a
k=3
catch B
Sample list 4-14のkを4にして実行した結果(cygwin)

% c++ try_samp3.cpp
% ./a
k=4
catch other
Sample list 4-14のkを5にして実行した結果(cygwin)

% c++ try_samp3.cpp
% ./a
k=5
Sample list 4-14のkを6にして実行した結果(cygwin)

% c++ try_samp3.cpp
% ./a
k=6

目次に戻る, 先頭に戻る


Copyright (c) 2006 Satoshi Uchida, The Office Uchida, School of Computer