C++第3版 8章 練習問題解答

目次へ戻る

8.1

#include <list>
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#pragma warning(disable : 4786)

namespace stringList {
    typedef vector<string> LType;
    typedef LType::iterator Iterator;
    LType lst;
    Iterator begin(){return lst.begin();}
    Iterator end(){return lst.end();}
    void sort(){std::sort(begin(),end());}
    void reverse(){std::reverse(begin(),end());}
    void push_back(const string& s){lst.push_back(s);}
}

namespace L = stringList;

int main()
{
    L::push_back("Pascal");
    L::push_back("C");
    L::push_back("Fortran");
    L::push_back("ALGOL");
    L::push_back("Perl");
    L::push_back("Java");
    L::push_back("COBOL");
    L::push_back("Smalltalk");
    L::Iterator i;
    L::sort();
    for (i=L::begin();i!=L::end();++i)
        cout << *i << " ";
    cout << endl;
    L::reverse();
    for (i=L::begin();i!=L::end();++i)
        cout << *i << " ";
    cout << endl;
}
Iterator を iterator としたいが、コンパイルできない。 pragma は例によって VC を黙らせるため。

8.2

namespace test {
#include <stdio.h>
}
int main()
{
    test::printf("ok\n");
}

8.3

namespace calc で括って、呼出し側で calc:: をつければよい。

8.4

#include <iostream>
using namespace std;

void f(){throw "test";}

int main()
{
    try {f();}
    catch (char* s) {cout << s << endl;}
}

8.5

#include <iostream>
using namespace std;

void g(int,int);
void f(int l, int i = -1)
{
    cout << "enter f " << i << endl;
    if (i < 0) g(l,10);
    else if (i) {
        g(l,i-1);
        if (i == l) throw "error f";
    }
}
void g(int l,int i)
{
    cout << "enter g " << i << endl;
    if (i > 0) f(l,i-1);
    if (i == l) throw "error g";
}

int main()
{
    try {f(3);}
    catch (char* s) {cout << s << endl;}
    try {f(4);}
    catch (char* s) {cout << s << endl;}
}

8.6

時間計測用のクラスのヘッダ
// etime.h
#include <sys/types.h>
class CETime {
        time_t m_time;
        unsigned short m_millitm;
public:
        inline void Reset();
        inline double Sec()const;
        CETime(){Reset();}
};
時間計測用のクラスのソース
// etime.cpp
#include "etime.h"
#include <sys/timeb.h>
void CETime::Reset()
{
        timeb   tb;
        ftime(&tb);
        m_time = tb.time;
        m_millitm = tb.millitm;
}

double CETime::Sec()const
{
        timeb   tb;
        ftime(&tb);
        return double(tb.time - m_time) + 0.001*(tb.millitm - m_millitm);
}
解答の本体のソース
#include "etime.h"
#include <string>
#include <iostream>
using namespace std;

void g(int,int);
void f(int l, int i = -1)
{
    string      s = "dumy";   // string 変数
    if (i < 0) g(l,10);
    else if (i) {
        g(l,i-1);
        if (i == l) throw "error f";
    }
}
void g(int l,int i)
{
    string      s = "dumy";   // string 変数
    if (i > 0) f(l,i-1);
    if (i == l) throw "error g";
}

int main()
{
    int         i,j, k[] = {10,0,-1}, n = 100000;
        CETime      e;
    for (j=0;j<3;++j) {
        e.Reset();
        for (i=0;i<n;++i) {
            try {f(k[j]);}
            catch (char*) {if (i%(n/10)==0) printf(".");}
        }
        cout << endl << e.Sec() << endl;
    }
}
CETime は、時間計測用のクラス。
string 変数1回目2回目3回目
なし6.35.80.3
あり16.521.05.8
上表は、1回目がすぐ抜ける時の時間(秒)、2回目が最後に抜ける時の時間、 3回目が例外を発生しない時の時間を表している。 関数 f と g の string 変数がないとき、1回目と2回目の時間は、 ほぼ同じであるから、ローカル変数がないときは、例外のコストは深さに依存しないと思われる。 逆に、デストラクタを持つローカル変数があるときは、 深いところで例外の発生する方がコストがかかるようである。 また、「ローカル変数の初期化+後処理」と「例外」のコストは、このケースでは同じぐらいである。

8.7

エラーが起きた時、Lexer::get_token で改行まで読み飛ばそうとしている。 しかし、get_token は、正常な入力を期待しており、エラーの時に呼出すと改行を読み飛ばす危険性がある。

8.8

etime.h は8.6を参照のこと。
#include "etime.h"
#include <iostream>
using namespace std;

char* f(int i)
{
    if (i) throw "error";
    return 0;
}

int main()
{
    int         i,j, n = 100000;
        CETime      e;
    for (j=0;j<2;++j) {
        e.Reset();
        for (i=0;i<n;++i) {
            try {f(j);}
            catch (char*) {if (i%(n/10)==0) printf(".");}
        }
        cout << endl << e.Sec() << endl;
    }
}
値を返す方は、0.05 秒、例外は 6.5 秒であった。

8.9

文法違反では、各関数が 0 を返すように設計されており、 例外を扱いやすい形にはなってないので省略する。

8.10

自信なし。
#include <limits>
using namespace std;

struct EOverFlow {};
struct EUnderFlow {};

double plus(double i,double j)
{
    double d = i+j;
    if (numeric_limits<double>::infinity() == d)
        throw EOverFlow();
    return d;
}
double minus(double i,double j)
{
    double d = i-j;
    if (numeric_limits<double>::infinity() == d)
        throw EOverFlow();
    return d;
}
double multiply(double i,double j)
{
    double d = i*j;
    if (numeric_limits<double>::infinity() == d)
        throw EOverFlow();
    if (i != 0 && j != 0 && d == 0)
        throw EUnderFlow();
    return d;
}
double divide(double i,double j)
{
    double d = i/j;
    if (numeric_limits<double>::infinity() == d)
        throw EOverFlow();
    if (i != 0 && d == 0)
        throw EUnderFlow();
    return d;
}

8.11

8.9と同じ理由で省略する。
前章目次次章