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

広告

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

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";
}
}

f09_0904.png




《その267》 補足(前回《266》の 関数テンプレートに関して)


 前回《266》 演習9-8(3)の問題文にある FixedArrayTest.cpp で、最初に出てくる関数テンプレート print_FixedArray がちゃんと動くのが、初心者の自分にはちょっと不思議な気がしたので、プログラムで確認してみました。


 下記のプログラムでは、
 ・1) クラステンプレート C を使って作成した 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);
}

f09_0061.png



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';
}

f09_0803.png




《その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';
}

f09_0802.png




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;
}
}

f09_0801.png




《その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";
}
}

f09_07.png




《その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';
}

f09_0051.png



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);
}

f09_0045.png



《その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();
}

f09_0044.png



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);
}

f09_0043.png




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

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

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

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

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


検索
<< 2018年01月 >>
  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日以上新しい記事の更新がないブログに表示されております。