C++第3版 7章 練習問題解答
-
7.1 , 7.2 , 7.3 ,
7.4 , 7.5 , 7.6 ,
7.7 , 7.8 , 7.9 ,
7.10 , 7.11 , 7.12 ,
7.13 , 7.14 , 7.15 ,
7.16 , 7.17 , 7.18 ,
7.19
目次へ戻る
#include <iostream>
using namespace std;
typedef void (*FType)(char*,int&);
void dumy(char*,int&){cout << "test\n";}
FType func(FType f){return f;}
int main()
{
FType f = dumy;
int i;
(*func(f))(0,i);
}
|
2つの int を引数に取り、int を返す関数の参照を型定義している。
参照なので必ず初期化しなければいけないので、
初期化を強制するメリットがある。
逆に言えば、融通が利かずわかりにくいというデメリットがある。
ところで、何故、
typedef int (*&rifii)(int,int);
では駄目なのであろうか?
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
while (--argc)
cout << "Hello, name " << *++argv << "!\n";
}
|
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
string s;
while (--argc) {
ifstream fi(*++argv);
while (getline(fi,s))
cout << s << endl;
}
}
|
お茶濁し
void ssort(void* base,size_t n,size_t sz,CFT cmp)
{
qsort(base,sz,n,cmp);
}
|
示された構造体では、空の状態を作れないので、入れ子クラスにした。
string を char* に変える場合は、string& と string を char* に、
比較を strcmp に変えれば良い。
#include <string>
#include <iostream>
using namespace std;
class Tree {
struct Tnode {
const string word;
int count;
Tnode* left;
Tnode* right;
Tnode(const string& w) : word(w)
{left = right = 0; count = 1;}
~Tnode(){delete left; delete right;}
void Print() {
if (left) left->Print();
cout << word << ":" << count << endl;
if (right) right->Print();
}
static void Push(Tnode*& p,const string& w) {
if (!p) p = new Tnode(w);
else if (w < p->word) Push(p->left,w);
else if (p->word < w) Push(p->right,w);
else ++p->count;
}
} *p;
Tree(const Tree&); // 定義なし
void operator=(const Tree&); // 定義なし
public:
Tree(){p = 0;}
~Tree(){delete p;}
void Insert(const string& w){Tnode::Push(p,w);}
void Print()const{if (p) p->Print();}
};
int main()
{
Tree t;
t.Insert("cat");
t.Insert("dog");
t.Insert("panda");
t.Insert("dog");
t.Insert("lion");
t.Print();
}
|
2次元配列の反転ってどうすればいいのだろう?
取りあえず、転置してみた。
#include <vector>
#include <iostream>
using namespace std;
template <typename T>
static void Print(const vector<vector<T> >& v)
{
vector<vector<T> >::const_iterator i;
for (i=v.begin();i!=v.end();++i) {
vector<T>::const_iterator j;
for (j=i->begin();j!=i->end();++j)
cout << *j << " ";
cout << endl;
}
}
template <typename T>
void Trans(vector<vector<T> >& v)
{
int i,j, n = v.size();
for (i=1;i<n;++i) for (j=0;j<n-i;++j)
swap(v[j][i+j],v[i+j][j]);
}
int main()
{
int i,j,n = 4;
vector<int> tmp(n);
vector<vector<int> > v(n,tmp);
for (i=0;i<n;++i) for (j=0;j<n;++j)
v[i][j] = i*n+j;
Print(v);
Trans(v);
Print(v);
}
|
DOS だと暗号化された文字が ^Z になると複合するときに
そこで入力終了になってしまう。
そこで setmode でバイナリモードにしている。
(UNIX なら必要ないはず。)
#include <io.h>
#include <fcntl.h>
#include <cstring>
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
char c, *key = argc > 1 ? argv[1] : "";
int i = 0, n = strlen(key);
if (!n) n = 1;
setmode(fileno(stdin),O_BINARY);
setmode(fileno(stdout),O_BINARY);
while (cin.get(c))
cout << static_cast<char>(c^key[i++ % n]);
}
|
ヒントは見てない。
key を片っ端から試して、変な文字にならなかったら OK としている。
暗号データは、ファイル crypt.txt から読み込む。
key の文字数は、とりあえず 3文字で固定。
#include <io.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#include <sys/stat.h>
int Check(int n,const char* dat,char* est,
const char* key,int sz)
{
int c,i;
for (i=0;i<n;++i) {
c = est[i] = dat[i]^key[i%sz];
if (c != ' ' && c != '\r' && c != '\n' &&
c != '.' && (c < 'a' || c > 'z')) return 0;
}
return 1;
}
int main()
{
const sz = 3; // key の長さ
int i,n,fd;
if (!(fd = open("crypt.txt",O_RDONLY | O_BINARY))) {
fprintf(stderr,"no crypt file\n");
return 1;
}
struct stat st;
if (fstat(fd,&st)) return 1;
char *dat,*est,key[sz+1];
dat = new char[n = st.st_size];
est = new char[n];
read(fd,dat,n);
close(fd);
for (i=0;i<sz;++i) key[i] = 'a';
while (true) {
if (Check(n,dat,est,key,sz)) {
printf("key = %s\n",key);
for (i=0;i<n;++i) printf("%c",est[i]);
break;
}
if (++key[(i = sz)-1] > 'z') {
while (--i) {
key[i] = 'a';
if (++key[i-1] <= 'z') break;
}
if (i == 0) break;
}
}
delete[] dat;
delete[] est;
return 0;
}
|
printf は使うなって書いてあるけど、vprintf は書いてないからなあ。
#include <cstdio>
#include <cstdarg>
void error(const char* fmt, ...)
{
va_list va;
va_start(va,fmt);
vprintf(fmt,va);
va_end(va);
}
int main()
{
error("%s %c %d\n","test",':',123);
}
|
例えば、function type のつもりで FType とする。
関数は大文字で始める。
何故なら、小文字で始めると、
標準ライブラリと名前がバッティングする可能性があるので。
変数名は小文字にする。メンバは m_ で始める。
それ以外は、アンダースコアはあまり使わず、
区切りは大文字にする。(例:GetWidth)
短い名前は、重要でない変数名で使う。
コーディングスタイルは、しばしば変えることがある。
使用するライブラリを変えるとき、そのライブラリに合わせて
コーディングスタイルを変えたりする。
6.23参照。
PI は const double を用いた方がよい。
MAX は、((a)>(b)?(a):(b)) のように括弧をつけた方が安全であるが、
それでも副作用の可能性があるので、inline もしくは、template にした方がよい。
fac は再帰的定義になっており、マクロでは書けない。
チャレンジの解答を参照のこと。
void print(int v,int b = 10)
{
char buf[80];
itoa(v,buf,b);
printf("%s\n",buf);
}
|
6.18参照のこと。あっ、引数のチェックはしていない。
|
int fac(int n)
{
int i = 1;
while (n > 1) i *= n--;
return i;
}
|
AddYear では、実際には存在しない年月日になる可能性があるが対処していない。
(10.1参照)
struct Date {
int year;
int month;
int day;
void Set(int y,int m,int d){year = y; month = m; day = d;}
Date(int y=0,int m=1,int d=1){Set(y,m,d);}
void AddYear(int i = 1){year += i;}
void AddMonth(int i = 1){month += i; i = (month-1)/12 - (month<0?1:0);
month -= i*12; if (i) AddYear(i);}
void AddDay(int i = 1){int d; day += i;
while (day < 1) {AddMonth(-1); day += Days();}
while (day > Days()) {day -= Days(); AddMonth();}}
int Days()const{int d[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
return month == 2 && IsLeap(year) ? 29 : d[month];}
int Week()const // 0 is Sunday
{int y = year, m = month-2;if (m < 1) {m += 12; --y;}
return (y + y/4 - y/100 + y/400 + int(m*2.6-.1) + day)%7;}
Date NextMonday()const{Date d = *this; int w = Week();
d.AddDay((7-w)%7+1); return d;}
Date CurrMonday()const{Date d = *this; d.AddDay(1-Week()); return d;}
bool operator<(const Date& d)const{return year < d.year || year == d.year
&& (month < d.month || month == d.month && day == d.day);}
bool operator==(const Date& d)const{return year == d.year &&
month == d.month && day == d.day;}
static bool IsLeap(int y)
{return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);}
};
|
前章へ
目次へ
次章へ