新規記事の投稿を行うことで、非表示にすることが可能です。
2018年01月31日
《その268》 問題演習 p.349演習9-9
新版明解C++中級編 p.349 演習9-9
下記のスタッククラステンプレート SimpleStack<> は、インクルードモデルで実現されています。このクラステンプレートに、以下のメンバ関数を追加せよ。
Type& peek() … 頂上のデータを削除することなく返す。
int capacity() … スタックの容量を返す。
bool is_full() … スタックが満杯であるかどうかを返す。
bool is_empty() … スタックが空であるかどうかを返す。
なお、例外が送出・捕捉されるような利用例のプログラムもあわせて作ること。
// SimpleStack.h
#ifndef ___Class_SimpleStack
#define ___Class_SimpleStack
// スタッククラステンプレート
template<class Type> class SimpleStack {
int size; // スタック容量
int ptr; // スタックポインタ
Type* stk; // 先頭要素へのポインタ
// コピーコンストラクタの無効化
SimpleStack(const SimpleStack<Type>&);
// 代入演算子の無効化
SimpleStack& operator=(const SimpleStack<Type>&);
public:
class Overflow { }; // Overflow例外
class Empty { }; // Empty例外
// コンストラクタ
explicit SimpleStack(int sz);
// デストラクタ
~SimpleStack();
// push
Type& push(const Type& x);
// pop
Type pop();
};
#include "SimpleStackImplementation.h"
#endif
// SimpleStackImplementation.h
#ifndef ___Class_SimpleStackImplementation
#define ___Class_SimpleStackImplementation
// コンストラクタ
template<class Type>
SimpleStack<Type>::SimpleStack(int sz)
: size(sz), ptr(0) {
stk = new Type[size];
}
// デストラクタ
template<class Type>
SimpleStack<Type>::~SimpleStack() {
delete[] stk;
}
// push
template<class Type>
Type& SimpleStack<Type>::push(const Type& x) {
if (ptr >= size)
throw Overflow();
return stk[ptr++] = x;
}
// pop
template<class Type>
Type SimpleStack<Type>::pop() {
if (ptr <= 0)
throw Empty();
return stk[--ptr];
}
#endif
// 解答
// SimpleStack.h
#ifndef ___Class_SimpleStack
#define ___Class_SimpleStack
// スタッククラステンプレート
template<class Type> class SimpleStack {
int size; // スタック容量
int ptr; // スタックポインタ
Type* stk; // 先頭要素へのポインタ
// コピーコンストラクタの無効化
SimpleStack(const SimpleStack<Type>&);
// 代入演算子の無効化
SimpleStack& operator=(const SimpleStack<Type>&);
public:
class Overflow { }; // Overflow例外
class Empty { }; // Empty例外
// コンストラクタ
explicit SimpleStack(int sz);
// デストラクタ
~SimpleStack();
// push
Type& push(const Type& x);
// pop
Type pop();
// ★頂上のデータを削除することなく返す。
Type& peek();
// ★スタックの容量を返す。
int capacity();
// ★スタックが満杯であるかどうかを返す。
bool is_full();
// ★スタックが空であるかどうかを返す。
bool is_empty();
};
#include "SimpleStackImplementation.h"
#endif
// SimpleStackImplementation.h
#ifndef ___Class_SimpleStackImplementation
#define ___Class_SimpleStackImplementation
// コンストラクタ
template<class Type>
SimpleStack<Type>::SimpleStack(int sz)
: size(sz), ptr(0) {
stk = new Type[size];
}
// デストラクタ
template<class Type>
SimpleStack<Type>::~SimpleStack() {
delete[] stk;
}
// push
template<class Type>
Type& SimpleStack<Type>::push(const Type& x) {
if (ptr >= size)
throw Overflow();
return stk[ptr++] = x;
}
// pop
template<class Type>
Type SimpleStack<Type>::pop() {
if (ptr <= 0)
throw Empty();
return stk[--ptr];
}
// ★頂上のデータを削除することなく返す。
template<class Type>
Type& SimpleStack<Type>::peek() {
if (ptr <= 0)
throw Empty();
int temp = ptr - 1;
return stk[temp];
}
// ★スタックの容量を返す。
template<class Type>
int SimpleStack<Type>::capacity() {
return size;
}
// ★スタックが満杯であるかどうかを返す。
template<class Type>
bool SimpleStack<Type>::is_full() {
return size == ptr;
}
// ★スタックが空であるかどうかを返す。
template<class Type>
bool SimpleStack<Type>::is_empty() {
return ptr == 0;
}
#endif
// SimpleStackTest.cpp
#include <string>
#include <iostream>
#include "SimpleStack.h"
using namespace std;
void push_it(SimpleStack<char>& s, char c) {
try {
s.push(c);
}
catch (const SimpleStack<char>::Overflow&) {
cout << "\a !満杯の<char>スタックに"
"プッシュしようとしました。\n";
}
}
void pop_it(SimpleStack<char>& s) {
try {
char c = s.pop();
cout << ' ' << c << '\n';
}
catch (const SimpleStack<char>::Empty&) {
cout << "\a !空の<char>スタックから"
"ポップしようとしました。\n";
}
}
void pop_all(SimpleStack<char>& s) {
if (s.is_empty()) {
pop_it(s); goto L;
}
while (!s.is_empty()) {
cout << ' ' << s.pop();
}
cout << '\n';
L:;
}
int main() {
int nn;
cout << "スタックの容量 : "; cin >> nn;
SimpleStack<char> s(nn);
cout << "容量 " << s.capacity()
<< " のスタックを準備しました。\n";
while (1) {
int f; char c;
cout << "◆ 1.push 2.pop 3.文字列をpush "
"4.頂上のデータ 5.全データをpop "
"0.終了 : "; cin >> f;
if (f == 1) {
cout << " 文字 : "; cin >> c;
push_it(s, c);
}
else if (f == 2)
pop_it(s);
else if (f == 3) {
string x;
cout << " 文字列:";
cin >> x;
for (unsigned i = 0; i < x.length(); i++)
push_it(s, x[i]);
}
else if (f == 4)
cout << ' ' << s.peek() << '\n';
else if (f == 5) {
pop_all(s);
}
else break;
if (s.is_full()) cout << " ※満\n";
if (s.is_empty()) cout << " ※空\n";
}
}
《その267》 補足(前回《266》の 関数テンプレートに関して)
前回《266》 演習9-8(3)の問題文にある FixedArrayTest.cpp で、最初に出てくる関数テンプレート print_FixedArray がちゃんと動くのが、初心者の自分にはちょっと不思議な気がしたので、プログラムで確認してみました。
下記のプログラムでは、
・1) クラステンプレート C を使って作成した C
関数テンプレート f に渡します。
・2) 関数テンプレート f は、そのオブジェクト c を 仮引数 C<T1, T2>& a で受け取ります。
関数テンプレート f は、この仮引数からの情報だけで T1, T2 の型を決定してくれるんですね。とりあえず理解できました。
初歩的な内容ですみません m(_ _)m
#include <iostream>
template <class Type1, class Type2> class C {
public:
Type1 t1;
Type2 t2;
C(Type1 x, Type2 y) : t1(x), t2(y) { }
};
template <class T1, class T2>
void f(C<T1, T2>& a) {
std::cout << typeid(T1).name() << '\n';
std::cout << typeid(T2).name() << '\n';
std::cout << a.t1 << ' ' << a.t2 << '\n';
}
int main() {
C<char, int> c('a', 100);
f(c);
}
2018年01月30日
《その266》 問題演習 p.349演習9-8 (3)
新版明解C++中級編 p.349 演習9-8
(1)〜(3)の各クラステンプレートをインクルードモデルで実現したプログラム を作成せよ。
(3)クラステンプレート FixedArray<>
// FixedArray.h
#ifndef ___ClassFixedArray
#define ___ClassFixedArray
// 配列クラステンプレート
// 配列の実体は、データメンバとして
// クラスオブジェクトに含まれる。
template <class Type, int N>
class FixedArray {
Type vec[N]; // 要素数 N の Type型配列
public:
// 添字範囲エラー
class IdxRngErr {
const FixedArray* ident;
int index;
public:
IdxRngErr(const FixedArray* p, int i)
: ident(p), index(i) { }
int Index() const { return index; }
};
// コンストラクタ
explicit FixedArray(const Type& v = Type()) {
for (int i = 0; i < N; i++)
vec[i] = v;
}
// コピーコンストラクタ
FixedArray(const FixedArray& x) {
if (&x != this) {
for (int i = 0; i < N; i++)
vec[i] = x.vec[i];
}
}
// 要素数を返却
int size() const { return N; }
// 代入演算子
FixedArray& operator=(const FixedArray& x) {
for (int i = 0; i < N; i++)
vec[i] = x.vec[i];
return *this;
}
// 添字演算子[]
Type& operator[](int i) {
if (i < 0 || i >= N)
throw IdxRngErr(this, i);
return vec[i];
}
// 添字演算子[]
const Type& operator[](int i) const {
if (i < 0 || i >= N)
throw IdxRngErr(this, i);
return vec[i];
}
};
#endif
// FixedArrayTest.cpp
#include <iomanip>
#include <iostream>
#include "FixedArray.h"
using namespace std;
// 関数テンプレート
template <class Type, int N>
void print_FixedArray(
const FixedArray<Type, N>& a
) {
cout << "{ ";
for (int i = 0; i < a.size(); i++)
cout << a[i] << ' ';
cout << "}";
}
int main() {
FixedArray<int, 7> a1;
for (int i = 0; i < a1.size(); i++)
a1[i] = i;
FixedArray<int, 7> a2 = a1;
cout << "a1 = ";
print_FixedArray(a1); cout << '\n';
cout << "a2 = ";
print_FixedArray(a2); cout << '\n';
}
問題で与えられた FixedArray.h を、FixedArray.h と FixedArrayImplementation.h の2つに分けて、
インクルードモデルを実現しました。
// 解答
// FixedArray.h
#ifndef ___ClassFixedArray
#define ___ClassFixedArray
// 配列クラステンプレート
// 配列の実態は、データメンバとして
// クラスオブジェクトに含まれる。
template <class Type, int N>
class FixedArray {
Type vec[N]; // 要素数 N の Type型配列
public:
// 添字範囲エラー
class IdxRngErr {
const FixedArray* ident;
int index;
public:
IdxRngErr(const FixedArray* p, int i);
int Index() const;
};
// コンストラクタ
explicit FixedArray(const Type& v = Type());
// コピーコンストラクタ
FixedArray(const FixedArray& x);
// 要素数を返却
int size() const;
// 代入演算子
FixedArray& operator=(const FixedArray& x);
// 添字演算子[]
Type& operator[](int i);
// 添字演算子[]
const Type& operator[](int i) const;
};
#include "FixedArrayImplementation.h"
#endif
// FixedArray.h
#ifndef ___ClassFixedArrayImplementation
#define ___ClassFixedArrayImplementation
template <class Type, int N>
FixedArray<Type, N>::IdxRngErr::
IdxRngErr(const FixedArray* p, int i)
: ident(p), index(i) { }
template <class Type, int N>
int FixedArray<Type, N>::IdxRngErr::
Index() const { return index; }
template <class Type, int N>
FixedArray<Type, N>::
FixedArray(const Type& v) {
for (int i = 0; i < N; i++)
vec[i] = v;
}
template <class Type, int N>
FixedArray<Type, N>::
FixedArray(const FixedArray& x) {
if (&x != this) {
for (int i = 0; i < N; i++)
vec[i] = x.vec[i];
}
}
template <class Type, int N>
int FixedArray<Type, N>::
size() const { return N; }
template <class Type, int N>
FixedArray<Type, N>& FixedArray<Type, N>::
operator=(const FixedArray& x) {
for (int i = 0; i < N; i++)
vec[i] = x.vec[i];
return *this;
}
template <class Type, int N>
Type& FixedArray<Type, N>::
operator[](int i) {
if (i < 0 || i >= N)
throw IdxRngErr(this, i);
return vec[i];
}
template <class Type, int N>
const Type& FixedArray<Type, N>::
operator[](int i) const {
if (i < 0 || i >= N)
throw IdxRngErr(this, i);
return vec[i];
}
#endif
// FixedArrayTest.cpp
#include <iomanip>
#include <iostream>
#include "FixedArray.h"
using namespace std;
// 関数テンプレート
template <class Type, int N>
void print_FixedArray(
const FixedArray<Type, N>& a
) {
cout << "{ ";
for (int i = 0; i < a.size(); i++)
cout << a[i] << ' ';
cout << "}";
}
int main() {
FixedArray<int, 7> a1;
for (int i = 0; i < a1.size(); i++)
a1[i] = i;
FixedArray<int, 7> a2 = a1;
cout << "a1 = ";
print_FixedArray(a1); cout << '\n';
cout << "a2 = ";
print_FixedArray(a2); cout << '\n';
}
《その265》 問題演習 p.349演習9-8 (2)
新版明解C++中級編 p.349 演習9-8
(1)〜(3)の各クラステンプレートをインクルードモデルで実現したプログラム を作成せよ。
(2)クラステンプレート Array<bool>
// Array.h
#ifndef ___ClassTemplateArray
#define ___ClassTemplateArray
template <class Type> class Array { };
#endif
// BoolArray.h
#ifndef ___ClassTemplateBoolArray
#define ___ClassTemplateBoolArray
#include <limits>
#include "Array.h"
template<> class Array<bool> {
typedef unsigned char BYTE;
static const int CHAR_BITS
= std::numeric_limits<unsigned char>::digits;
// bool型配列の要素数
int nelem;
// bool型配列を格納するためのBYTE型配列の要素数
int velem;
// BYTE型先頭要素へのポインタ
BYTE* vec;
// バイト型配列要素の必要数を算出
static int size_of(int sz) {
return (sz + CHAR_BITS - 1) / CHAR_BITS;
}
public:
// 該当バイト中の該当ビットへの参照を表すクラス
class BitOfByteRef {
BYTE& vec; // 参照先BYTE
int idx; // 参照先BYTE中のビット番号
public:
// コンストラクタ
BitOfByteRef(BYTE& r, int i)
: vec(r), idx(i) { }
// 該当ビットを bool型で返却
operator bool() const { return (vec >> idx) & 1U; }
// 該当ビットに true または false をセット
BitOfByteRef& operator=(bool b) {
if (b)
vec |= 1U << idx;
else
vec &= ~(1U << idx);
return *this;
}
};
// 添字範囲エラー
class IdxRngErr {
const Array* ident;
int index;
public:
IdxRngErr(const Array* p, int i) : ident(p), index(i) { }
int Index() const { return index; }
};
// コンストラクタ
explicit Array(int sz, bool v = bool())
: nelem(sz), velem(size_of(sz)) {
vec = new BYTE[velem];
// 全要素を初期化
for (int i = 0; i < velem; i++)
vec[i] = v;
}
// コピーコンストラクタ
Array(const Array& x) {
if (&x == this) {
nelem = 0;
vec = NULL;
}
else {
nelem = x.nelem;
velem = x.velem;
vec = new BYTE[velem];
for (int i = 0; i < velem; i++)
vec[i] = x.vec[i];
}
}
// デストラクタ
~Array() { delete[] vec; }
// 要素数を返す
int size() const { return nelem; }
// 代入演算子
Array& operator=(const Array& x) {
if (&x != this) {
if (velem != x.velem) {
delete[] vec;
velem = x.velem;
vec = new BYTE[velem];
}
nelem = x.nelem;
for (int i = 0; i < velem; i++)
vec[i] = x.vec[i];
}
return *this;
}
// 添字演算子[]
BitOfByteRef operator[](int i) {
if (i < 0 || i >= nelem)
throw IdxRngErr(this, i);
return BitOfByteRef(
vec[i / CHAR_BITS], (i & (CHAR_BITS - 1))
);
}
// 添字演算子[]
bool operator[](int i) const {
if (i < 0 || i >= nelem)
throw IdxRngErr(this, i);
return
(vec[i / CHAR_BITS] >> (i & (CHAR_BITS - 1)) & 1U)
== 1;
}
};
#endif
// BoolArrayTest.cpp
#include <iostream>
#include "BoolArray.h"
using namespace std;
void print_Array_bool(const Array<bool>& a) {
for (int i = 0; i < a.size(); i++)
cout << (a[i] ? '1' : '0');
}
int main() {
Array<bool> x(10);
cout << boolalpha;
for (int i = 0; i < x.size(); i++)
cout << "x[" << i << "] = "
<< x[i] << '\n';
Array<bool> y(x);
Array<bool> z(5);
z = y;
y[3] = true; y[6] = true;
z[5] = true; z[7] = true;
cout << "x = ";
print_Array_bool(x); cout << '\n';
cout << "y = ";
print_Array_bool(y); cout << '\n';
cout << "z = ";
print_Array_bool(z); cout << '\n';
}
問題で与えられた BoolArray.h を、BoolArray.h と BoolArrayImplementation.h の2つに分けて、
インクルードモデルを実現しました。
// 解答
// Array.h
#ifndef ___ClassTemplateArray
#define ___ClassTemplateArray
template <class Type> class Array { };
#endif
// BoolArray.h
#ifndef ___ClassTemplateBoolArray
#define ___ClassTemplateBoolArray
#include <limits>
#include "Array.h"
template<> class Array<bool> {
typedef unsigned char BYTE;
static const int CHAR_BITS
= std::numeric_limits<unsigned char>::digits;
// bool型配列の要素数
int nelem;
// bool型配列を格納するためのBYTE型配列の要素数
int velem;
// BYTE型先頭要素へのポインタ
BYTE* vec;
// バイト型配列要素の必要数を算出
static int size_of(int sz);
public:
// 該当バイト中の該当ビットへの参照を表すクラス
class BitOfByteRef {
BYTE& vec; // 参照先BYTE
int idx; // 参照先BYTE中のビット番号
public:
// コンストラクタ
BitOfByteRef(BYTE& r, int i);
// 該当ビットを bool型で返却
operator bool() const;
// 該当ビットに true または false をセット
BitOfByteRef& operator=(bool b);
};
// 添字範囲エラー
class IdxRngErr {
const Array* ident;
int index;
public:
IdxRngErr(const Array* p, int i);
int Index() const;
};
// コンストラクタ
explicit Array(int sz, bool v = bool());
// コピーコンストラクタ
Array(const Array& x);
// デストラクタ
~Array();
// 要素数を返す
int size() const;
// 代入演算子
Array& operator=(const Array& x);
// 添字演算子[]
BitOfByteRef operator[](int i);
// 添字演算子[]
bool operator[](int i) const;
};
#include "BoolArrayImplementation.h"
#endif
// BoolArrayImplementation.h
#ifndef ___ClassTemplateBoolArrayImplementation
#define ___ClassTemplateBoolArrayImplementation
#include "Array.h"
int Array<bool>::size_of(int sz) {
return (sz + CHAR_BITS - 1) / CHAR_BITS;
}
Array<bool>::BitOfByteRef::
BitOfByteRef(BYTE& r, int i)
: vec(r), idx(i) { }
Array<bool>::BitOfByteRef::
operator bool() const {
return (vec >> idx) & 1U;
}
Array<bool>::BitOfByteRef& Array<bool>::
BitOfByteRef::operator=(bool b) {
if (b)
vec |= 1U << idx;
else
vec &= ~(1U << idx);
return *this;
}
Array<bool>::IdxRngErr::
IdxRngErr(const Array<bool>* p, int i)
: ident(p), index(i) { }
int Array<bool>::IdxRngErr::
Index() const { return index; }
Array<bool>::Array(int sz, bool v)
: nelem(sz), velem(size_of(sz)) {
vec = new BYTE[velem];
for (int i = 0; i < velem; i++)
vec[i] = v;
}
Array<bool>::Array(const Array<bool>& x) {
if (&x == this) {
nelem = 0;
vec = NULL;
}
else {
nelem = x.nelem;
velem = x.velem;
vec = new BYTE[velem];
for (int i = 0; i < velem; i++)
vec[i] = x.vec[i];
}
}
Array<bool>::~Array() { delete[] vec; }
int Array<bool>::size() const { return nelem; }
Array<bool>& Array<bool>::
operator=(const Array<bool>& x) {
if (&x != this) {
if (velem != x.velem) {
delete[] vec;
velem = x.velem;
vec = new BYTE[velem];
}
nelem = x.nelem;
for (int i = 0; i < velem; i++)
vec[i] = x.vec[i];
}
return *this;
}
Array<bool>::BitOfByteRef Array<bool>::
operator[](int i) {
if (i < 0 || i >= nelem)
throw IdxRngErr(this, i);
return BitOfByteRef(
vec[i / CHAR_BITS],
(i & (CHAR_BITS - 1))
);
}
bool Array<bool>::operator[](int i) const {
if (i < 0 || i >= nelem)
throw IdxRngErr(this, i);
return
(vec[i / CHAR_BITS] >>
(i & (CHAR_BITS - 1)) & 1U)
== 1;
}
#endif
// BoolArrayTest.cpp
#include <iostream>
#include "BoolArray.h"
using namespace std;
void print_Array_bool(const Array<bool>& a) {
for (int i = 0; i < a.size(); i++)
cout << (a[i] ? '1' : '0');
}
int main() {
Array<bool> x(10);
cout << boolalpha;
for (int i = 0; i < x.size(); i++)
cout << "x[" << i << "] = "
<< x[i] << '\n';
Array<bool> y(x);
Array<bool> z(5);
z = y;
y[3] = true; y[6] = true;
z[5] = true; z[7] = true;
cout << "x = ";
print_Array_bool(x); cout << '\n';
cout << "y = ";
print_Array_bool(y); cout << '\n';
cout << "z = ";
print_Array_bool(z); cout << '\n';
}
2018年01月29日
《その264》 問題演習 p.349演習9-8 (1)
新版明解C++中級編 p.349 演習9-8
(1)〜(3)の各クラステンプレートをインクルードモデルで実現したプログラムを作成せよ。
(1)クラステンプレート Array<>
// Array.h
#ifndef ___ClassTemplate_Array
#define ___ClassTemplate_Array
template <class Type> class Array {
int nelem; // 配列要素数
Type* vec; // 配列先頭要素へのポインタ
// 添字として適なら true、否なら false
bool is_valid_index(int idx) {
return idx >= 0 && idx < nelem;
}
public:
// 添字範囲例外クラス
class IdxRngErr {
const Array* ident;
int idx;
public:
IdxRngErr(const Array* p, int i)
: ident(p), idx(i) { }
int Index() const { return idx; }
};
// コンストラクタ
explicit Array(int size, const Type& v = Type())
: nelem(size) {
vec = new Type[nelem];
for (int i = 0; i < nelem; i++)
vec[i] = v; // 全要素の初期値 v
}
// コピーコンストラクタ
Array(const Array<Type>& x) {
if (&x == this) {
nelem = 0;
vec = NULL;
}
else {
nelem = x.nelem;
vec = new Type[nelem];
for (int i = 0; i < nelem; i++)
vec[i] = x.vec[i];
}
}
// デストラクタ
~Array() { delete[] vec; }
// 要素数 nelem のゲッタ
int size() const { return nelem; }
// 代入演算子
Array& operator=(const Array<Type>& x) {
if (&x != this) {
if (nelem != x.nelem) {
delete[] vec;
nelem = x.nelem;
vec = new Type[nelem];
}
for (int i = 0; i < nelem; i++)
vec[i] = x.vec[i];
}
return *this;
}
// 添字演算子[]
Type& operator[](int i) {
// 添字が否なら、添字範囲例外を throw
if (!is_valid_index(i))
throw IdxRngErr(this, i);
return vec[i];
}
// 添字演算子[]
const Type& operator[](int i) const {
// 添字が否なら、添字範囲例外を throw
if (!is_valid_index(i))
throw IdxRngErr(this, i);
return vec[i];
}
};
#endif
// ArrayTest.cpp
#include <iostream>
#include "Array.h"
using namespace std;
int main() {
try {
int no;
Array<int> x(5);
Array<double> y(8);
cout << "データ数:";
cin >> no;
for (int i = 0; i < no; i++) {
x[i] = i;
y[i] = 0.1 * i;
cout << "x[" << i << "] = " << x[i] << " "
<< "y[" << i << "] = " << y[i] << '\n';
}
}
catch (bad_alloc) {
cout << "メモリの確保に失敗しました。\n";
return 1;
}
catch (const Array<int>::IdxRngErr& x) {
cout << "添字オーバフロー Array<int>:"
<< x.Index() << '\n';
return 1;
}
catch (const Array<double>::IdxRngErr& x) {
cout << "添字オーバフロー Array<double>:"
<< x.Index() << '\n';
return 1;
}
}
問題で与えられた Array.h を、Array.h と ArrayImplementation.h の2つに分けて、
インクルードモデルを実現しました。
// 解答
// Array.h
#ifndef ___ClassTemplateArray
#define ___ClassTemplateArray
template <class Type> class Array {
int nelem; // 配列要素数
Type* vec; // 配列先頭要素へのポインタ
// 添字として適なら true、否なら false
bool is_valid_index(int idx);
public:
// 添字範囲例外クラス
class IdxRngErr {
const Array* ident;
int idx;
public:
IdxRngErr(const Array* p, int i);
int Index() const;
};
// コンストラクタ
explicit Array(int size, const Type& v = Type());
// コピーコンストラクタ
Array(const Array<Type>& x);
// デストラクタ
~Array();
// 要素数 nelem のゲッタ
int size() const;
// 代入演算子
Array& operator=(const Array<Type>& x);
// 添字演算子[]
Type& operator[](int i);
// 添字演算子[]
const Type& operator[](int i) const;
};
#include "ArrayImplementation.h"
#endif
// ArrayImplementation.h
#ifndef ___ClassTemplateArrayImplementation
#define ___ClassTemplateArrayImplementation
template <class Type>
bool Array<Type>::is_valid_index(int idx) {
return idx >= 0 && idx < nelem;
}
template <class Type>
Array<Type>::IdxRngErr
::IdxRngErr(const Array* p, int i)
: ident(p), idx(i) { }
template <class Type>
int Array<Type>::IdxRngErr
::Index() const { return idx; }
template<class Type>
Array<Type>::Array(int size, const Type& v)
: nelem(size) {
vec = new Type[nelem];
for (int i = 0; i < nelem; i++)
vec[i] = v;
}
template<class Type>
Array<Type>::Array(const Array<Type>& x) {
if (&x == this) {
nelem = 0;
vec = NULL;
}
else {
nelem = x.nelem;
vec = new Type[nelem];
for (int i = 0; i < nelem; i++)
vec[i] = x.vec[i];
}
}
template<class Type>
Array<Type>::~Array() { delete[] vec; }
template<class Type>
int Array<Type>::size() const {
return nelem;
}
template<class Type>
Array<Type>& Array<Type>
::operator=(const Array<Type>& x) {
if (&x != this) {
if (nelem != x.nelem) {
delete[] vec;
nelem = x.nelem;
vec = new Type[nelem];
}
for (int i = 0; i < nelem; i++)
vec[i] = x.vec[i];
}
return *this;
}
template<class Type>
Type& Array<Type>::operator[](int i) {
if (!is_valid_index(i))
throw IdxRngErr(this, i);
return vec[i];
}
template<class Type>
const Type& Array<Type>
::operator[](int i) const {
if (!is_valid_index(i))
throw IdxRngErr(this, i);
return vec[i];
}
#endif
// ArrayTest.cpp
#include <iostream>
#include "Array.h"
using namespace std;
int main() {
try {
int no;
Array<int> x(5);
Array<double> y(8);
cout << "データ数:";
cin >> no;
for (int i = 0; i < no; i++) {
x[i] = i;
y[i] = 0.1 * i;
cout << "x[" << i << "] = " << x[i] << " "
<< "y[" << i << "] = " << y[i] << '\n';
}
}
catch (bad_alloc) {
cout << "メモリの確保に失敗しました。\n";
return 1;
}
catch (const Array<int>::IdxRngErr& x) {
cout << "添字オーバフロー Array<int>:"
<< x.Index() << '\n';
return 1;
}
catch (const Array<double>::IdxRngErr& x) {
cout << "添字オーバフロー Array<double>:"
<< x.Index() << '\n';
return 1;
}
}
《その263》 インクルードモデル & p.349演習9-7
インクルードモデル
規模の大きいクラステンプレートで、単一のヘッダでの管理が難しい場合、その対処法の1つに、インクルードモデルと呼ばれる手法があります。
この手法では、2つのヘッダを使います。一方のヘッダで、クラスの定義(メンバ関数は宣言のみにしておく)を行い、もう一方のヘッダでは、実装作業的なメンバ関数の定義等を行います。
インクルードモデルの具体的な手法については、今回の 演習9-7 と次回の 演習9-8 が参考になると思います。
新版明解C++中級編 p.349 演習9-7
下記のクラステンプレート Twin<> をインクルードモデルで実現したプログラムを作成せよ。
// Twin.h
#ifndef ___Class_Twin
#define ___Class_Twin
#include <utility>
#include <algorithm>
template <class Type> class Twin {
Type v1;
Type v2;
public:
Twin(const Type& f = Type(), const Type& s = Type())
: v1(f), v2(s) { }
Twin(const Twin<Type>& t)
: v1(t.first()), v2(t.second()) { }
Type first() const { return v1; } // v1 のゲッタ
Type& first() { return v1; } // v1 のゲッタ かつ セッタ
Type second() const { return v2; } // v2 のゲッタ
Type& second() { return v2; } // v2 のゲッタ かつ セッタ
void set(const Type& f, const Type& s) {
v1 = f; v2 = s;
}
Type min() const { return v1 < v2 ? v1 : v2; }
bool ascending() const { return v1 < v2; }
void sort() { if (!(v1 < v2)) std::swap(v1, v2); }
};
template <class Type> inline std::ostream& operator<<(
std::ostream& os, const Twin<Type>& t
) {
return os << "[" << t.first()
<< ", " << t.second() << "]";
}
#endif
// TwinTest.cpp
#include <string>
#include <iostream>
#include "Twin.h"
using namespace std;
int main()
{
const Twin<int> t1(15, 37);
cout << "t1 = " << t1 << '\n';
Twin<string> t2("ABC", "XYZ");
cout << "t2 = " << t2 << "\n\n";
cout << "t2の値を変更します。\n";
cout << "新しい第一値:";
cin >> t2.first();
cout << "新しい第二値:";
cin >> t2.second();
if (!t2.ascending()) {
cout << "第一値<第二値 が成立"
"しないのでソートします。\n";
t2.sort();
cout << "t2は" << t2
<< "に変更されました。\n";
}
}
問題で与えられた Twin.h を、Twin.h と TwinImplementation.h の2つに分けて、
インクルードモデルを実現しました。
// 解答
// Twin.h
#ifndef ___Class_Twin
#define ___Class_Twin
#include <utility>
#include <algorithm>
template <class Type> class Twin {
Type v1;
Type v2;
public:
Twin(
const Type& f = Type(),
const Type& s = Type()
);
Twin(const Twin<Type>& t);
Type first() const;
Type& first();
Type second() const;
Type& second();
void set(const Type& f, const Type& s);
Type min() const;
bool ascending() const;
void sort();
};
// ソース部をインクルード
#include "TwinImplementation.h"
#endif
// TwinImplementation.h
#ifndef ___Class_TwinImplementation
#define ___Class_TwinImplementation
template<class Type>
Twin<Type>::Twin(
const Type& f,
const Type& s
) : v1(f), v2(s) { }
template<class Type>
Twin<Type>::Twin(const Twin<Type>& t)
: v1(t.first()), v2(t.second()) { }
template<class Type>
Type Twin<Type>::first() const { return v1; }
template<class Type>
Type& Twin<Type>::first() { return v1; }
template<class Type>
Type Twin<Type>::second() const { return v2; }
template<class Type>
Type& Twin<Type>::second() { return v2; }
template<class Type>
void Twin<Type>::set(const Type& f, const Type& s) {
v1 = f; v2 = s;
}
template<class Type>
Type Twin<Type>::min() const { return v1 < v2 ? v1 : v2; }
template<class Type>
bool Twin<Type>::ascending() const { return v1 < v2; }
template<class Type>
void Twin<Type>::sort() { if (!(v1 < v2)) std::swap(v1, v2); }
template <class Type> inline std::ostream& operator<<(
std::ostream& os, const Twin<Type>& t
) {
return os << "[" << t.first()
<< ", " << t.second() << "]";
}
#endif
// TwinTest.cpp
#include <string>
#include <iostream>
#include "Twin.h"
using namespace std;
int main()
{
const Twin<int> t1(15, 37);
cout << "t1 = " << t1 << '\n';
Twin<string> t2("ABC", "XYZ");
cout << "t2 = " << t2 << "\n\n";
cout << "t2の値を変更します。\n";
cout << "新しい第一値:";
cin >> t2.first();
cout << "新しい第二値:";
cin >> t2.second();
if (!t2.ascending()) {
cout << "第一値<第二値 が成立"
"しないのでソートします。\n";
t2.sort();
cout << "t2は" << t2
<< "に変更されました。\n";
}
}
《その262》 仮引数が「型」でないテンプレート
仮引数が「型」でないテンプレート
下記のプログラムは、Array1.h,Array2.h の2つのヘッダを含みます。
この2つのヘッダには、それぞれ、
template <class Type> class Array1
template <class Type, int N> class Array2
というクラステンプレートが記述されています。
この2種類のテンプレートは、共に、任意の型の配列を作成します。両方の働きは似ていますが、配列本体の格納方法の違いから、構造的には全く異なるものになっています。
Array1 は、前にも(《249》等)扱ったことのあるクラステンプレートですが、
Array2 のほうは、テンプレート仮引数 <class Type, int N> が、<型, 整数> の形式であり、仮引数が「型」でないテンプレートになっています。
なお、Array1,Array2 は、コードを短くするために、コピーコンストラクタや代入演算子等の機能を削ぎ落として、必要最小限の機能だけ持たせてあります。
// Array1.h
#ifndef ___ClassTemplate_Array1
#define ___ClassTemplate_Array1
template <class Type> class Array1 {
int nelem; // 配列要素数
Type* vec; // 配列先頭要素へのポインタ
// コピー抑止
Array1(const Array1<Type>& x);
Array1& operator=(const Array1<Type>& x);
public:
// コンストラクタ
explicit Array1(int size, const Type& v = Type())
: nelem(size) {
vec = new Type[nelem];
for (int i = 0; i < nelem; i++)
vec[i] = v;
}
// デストラクタ
~Array1() { delete[] vec; }
// 添字演算子[]
Type& operator[](int i) {
return vec[i];
}
// 添字演算子[]
const Type& operator[](int i) const {
return vec[i];
}
};
#endif
// Array2.h
#ifndef ___ClassTemplate_Array2
#define ___ClassTemplate_Array2
template <class Type, int N> class Array2 {
Type vec[N]; // 配列要素数 N
// コピー抑止
Array2(const Array2& x);
Array2& operator=(const Array2& x);
public:
// コンストラクタ
explicit Array2(const Type& v = Type()) {
for (int i = 0; i < N; i++)
vec[i] = v;
}
// 添字演算子[]
Type& operator[](int i) {
return vec[i];
}
// 添字演算子[]
const Type& operator[](int i) const {
return vec[i];
}
};
#endif
// test.cpp
#include <iostream>
#include "Array1.h"
#include "Array2.h"
using namespace std;
int main() {
Array1<int> a1(10);
for (int i = 0; i < 10; i++) {
a1[i] = i + 10;
cout << a1[i] << ' ';
}
cout << '\n';
Array2<int, 10> a2;
for (int i = 0; i < 10; i++) {
a2[i] = i + 10;
cout << a2[i] << ' ';
}
cout << '\n';
}
2018年01月28日
《その261》 テンプレートクラス内のクラステンプレート(2)
テンプレートクラス内のクラステンプレート(クラスの外部でも定義)
前回《260》のプログラムでは、クラステンプレートの内部だけで、入れ子のクラステンプレートの定義を行いました。
今回のプログラムは、入れ子のクラステンプレートの定義を、外側のクラステンプレートの外部でも行っています。
この場合、テンプレートパラメーターを、入れ子の深さに応じて、
template <class T1> template <class T2>
のように繰り返して記述しなければなりません。
以下はプログラムです。
#include <string>
#include <iostream>
using namespace std;
template <class T1> class Outer {
public:
template <class T2> class Inner {
public:
void disp(T2 t2);
};
Inner<int> i_int;
Inner<char> i_chr;
Inner<double> i_dbl;
T1 t1;
Outer(T1 x) : t1(x) { }
};
template <class T1> template <class T2>
void Outer<T1>::Inner<T2>::disp(T2 t2) {
cout << "◆「 "<< t2 << " 」は、"
<< typeid(t2).name() << "型の値です。\n";
}
int main() {
Outer<string> o_int = Outer<string>("メモ");
cout << o_int.t1 << '\n';
o_int.i_int.disp(12345);
o_int.i_chr.disp('s');
o_int.i_dbl.disp(6.62607);
}
《その260》 テンプレートクラス内のクラステンプレート(1)
テンプレートクラス内のクラステンプレート(クラスの内部で定義)
テンプレートクラスの内部で、クラステンプレートの定義を行うことができます。
※テンプレートクラスの定義を 外部で行うこともできます(次回)。
以下はプログラムです。
#include <string>
#include <iostream>
using namespace std;
template <class T> class Outer {
public:
T t;
// 内部のクラステンプレート
template <class U> class Inner {
public:
U u;
// コンストラクタ
Inner(U x) : u(x) { }
void f1() { cout << u << '\n'; }
};
Inner<char> c;
Inner<double> d;
// コンストラクタ
Outer(T x, char y, double z)
: t(x), c(y), d(z) { }
void f2() {
cout << t;
c.f1();
}
};
int main() {
Outer<string> obj1("abcde", 'F', 1.38065);
obj1.f2();
obj1.d.f1();
cout << '\n';
Outer<int> obj2(12345, '6', 6.67408);
obj2.f2();
obj2.d.f1();
}
2018年01月27日
《その259》 テンプレートクラス内の関数テンプレート
テンプレートクラス内の関数テンプレート
テンプレートクラスの内部で、関数テンプレートの定義や宣言を行うことができます。
下記のプログラムでは、テンプレートクラス内部で、
・関数テンプレートの定義を行う場合 と、
・関数テンプレートの宣言のみを行い 定義はテンプレートクラスの外で行う場合
の2通りの方法を実施しています。
以下はプログラムです。
#include <string>
#include <iostream>
using namespace std;
template <class Type> class C {
public:
Type t; // データメンバ
// クラステンプレート内で、関数テンプレート f1 を定義
template <class T1> void f1(T1 x) {
cout << t << ' ' << x << '\n';
}
// クラステンプレート内で、関数テンプレート f2 を宣言
// (定義は クラス の外)
template <class T2> void f2(T2 x);
// コンストラクタ
C(Type x) : t(x) { }
};
// クラス の外で、メンバテンプレート関数 f2 を定義
template <class Type>
template <class T2> void C<Type>::f2(T2 x) {
cout << t << ' ' << x << '\n';
}
int main() {
C<string> abc("ABCDEF");
abc.f1("GHIJKL");
abc.f1(3.14);
cout << '\n';
abc.f2("GHIJKL");
abc.f2(3.14);
}