复习C++

自定义数据类型最灵活的方式就是使用C++的类结构

现在定义一个货币类型Currency:

enum signType{PLUS,MINUS};

class Currency {
public:
    Currency(signType theSign = PLUS,
             unsigned long theDollars = 0,
             unsigned int theCents = 0);
    ~Currency() {}
    void setValue(signType,unsigned long,unsigned int);
    void setValue(double);
    signType getSign() const { return sign; }
    unsigned long getDollars() const { return dollars; }
    unsigned int getCents() const { return cents; }
    Currency add(const Currency&) const;
    Currency& increment(const Currency&);
    void output() const;
private:
    signType sign;
    unsigned long dollars;
    unsigned int cents;
};

类的成员声明有两部分: public 和 private,分别代表公有和私有,公有部分所声明的是来操作类对象(或实例)的成员函数(方法),它们对类的用户是可见的,是用户与类对象进行交互的唯一手段。私有部分声明的是用户不可见的数据成员和成员函数,公有部分的地一个成员函数与类名相同

名称与类名相同的成员函数为构造函数,指明了第一个创建类对象的方法,无返回值,以~为前缀的函数是析构函数

set前缀的函数供给用户为类对象赋值,get前缀的函数返回调用对象的相应数据成员,关键字const指明这些函数不会改变调用对象的值,这种函数称为常量函数

成员函数add将调用对象的货币值与参数对象的货币值相加,然后返回相加后的结果,因为该成员函数不会改变调用对象的值,所以是一个常量函数

成员函数increment将参数对象的货币值加到调用对象上,改变了调用对象的值,所以不是一个常量函数

实现构造函数, 调用setValue函数进行赋值:

Currency::Currency(signType theSign, unsigned long theDollars, unsigned int theCents) {
    setValue(theSign,theDollars,theCents);
}

对于setValue函数有两个重载,第一个成员函数setValue首先验证参数值的合法性,只有参数值合法,才能用来给调用对象的私有数据成员赋值,如果参数不合法,就抛出一个类型为illegalParameterValue的异常

第二个成员函数setValue不验证参数值的合法性,仅使用小数点后面头两个数字,对于形如d1.d2d3的数,用计算机表示可能就是不精确的,比如应计算机表示数值5.29,实际存储的值可能比5.29略小,如果像下面这样抽取美分的值可能要出错:

cents = (unsigned int)((theAmount - dollars) * 100);

因为(theAmount-dollars)*100要比29稍微小一点,当程序将其转化为一个整数时,赋给cents的值是28而不是29,解决这个问题的方法是给theAmount加上0.001,这时,只要d1.d2d3用计算机表示后与实际值相比不少于0.001或不多于0.009,结果是正确的

void Currency::setValue(signType theSign, unsigned long theDollars, unsigned int theCents) {
    if (theCents > 99) {
        throw illegalParametrValue("Cents should be < 100");
    }
    sign = theSign;
    dollars = theDollars;
    cents = theCents;
}

void Currency::setValue(double theAmount) {
    if (theAmount < 0) {
        sign = MINUS;
        theAmount = -theAmount;
    } else {
        sign = PLUS;
    }
    dollars = (unsigned long) theAmount;
    cents = (unsigned int) ((theAmount + 0.001 - dollars) * 100);
}

接下来的add的代码:

Currency Currency::add(const Currency &x) const {
    long a1,a2,a3;
    Currency result;
    a1 = dollars * 100 + cents;
    if (sign == MINUS) a1 = -a1;
    a2 = x.dollars * 100 + x.cents;
    if (x.sign == MINUS) a2 = -a2;
    a3 = a1 + a2;
    if (a3 < 0) {
        result.sign = MINUS;
        a3 = -a3;
    } else {
        result.sign = PLUS;
    }
    result.dollars = a3/100;
    result.cents = a3 - result.dollars * 100;

    return result;
}

首先要将相加的两个对象转化为整数,result是局部对象,作为返回值必须被复制到调用环境中,此处是值返回

下面是increment和output的代码:

Currency& Currency::increment(const Currency &x) {
    *this = add(x);
    return *this;
}

void Currency::output() const {
    if (sign == MINUS) cout << '-';
    cout << '$' << dollars << '.';
    if (cents < 10) cout << '0';
    cout << cents;
}

保留关键字this指向调用对象,*this即调用对象,这里调用add函数,将x与调用对象相加,然后将相加的结果赋值给this,这个对象不是局部对象,当函数结束时,空间不会自动释放,所以返回引用

另一种写法:

enum signType{PLUS,MINUS};

class Currency {
public:
    Currency(signType theSign = PLUS,
             unsigned long theDollars = 0,
             unsigned int theCents = 0);
    ~Currency() {}
    void setValue(signType,unsigned long,unsigned int);
    void setValue(double);
    signType getSign() const { return amount < 0? MINUS:PLUS; }
    unsigned long getDollars() const { return (amount < 0? -amount:amount) / 100; }
    unsigned int getCents() const { return (amount < 0? -amount:amount)-getDollars()*100; }
    Currency add(const Currency&) const;
    Currency& increment(const Currency&);
    void output() const;
    Currency operator+(const Currency&) const;
    Currency operator+=(const Currency& x) { amount += x.amount}
private:
    long amount;
};

Currency::Currency(signType theSign, unsigned long theDollars, unsigned int theCents) {
    setValue(theSign,theDollars,theCents);
}

void Currency::setValue(signType theSign, unsigned long theDollars, unsigned int theCents) {
    if (theCents > 99) {
        throw illegalParametrValue("Cents should be < 100");
    }
    amount = theDollars * 100 + theCents;
    if (theSign == MINUS) amount -= amount;
}

void Currency::setValue(double theAmount) {
    if (theAmount < 0) {
        amount = (long) ((theAmount - 0.001) * 100);
    } else {
        amount = (long) ((theAmount + 0.001) * 100);
    }
}

Currency Currency::add(const Currency &x) const {
    Currency y;
    y.amount = amount + x.amount;
    return y;
}

操作符重载

借助操作符重载,使用+和+=替代add和increment,成员函数output用一个输出流的名字作为参数

class Currency {
public:
    Currency(signType theSign = PLUS,
             unsigned long theDollars = 0,
             unsigned int theCents = 0);
    ~Currency() {}
    void setValue(signType,unsigned long,unsigned int);
    void setValue(double);
    signType getSign() const { return amount < 0? MINUS:PLUS; }
    unsigned long getDollars() const { return (amount < 0? -amount:amount) / 100; }
    unsigned int getCents() const { return (amount < 0? -amount:amount)-getDollars()*100; }
    Currency add(const Currency&) const;
    Currency& increment(const Currency&);
    void output(ostream &out) const;
    Currency operator+(const Currency&) const;
    Currency operator+=(const Currency& x) { amount += x.amount; return *this; }
private:
    long amount;
};

void Currency::output(ostream& out) const {
    long theAmount = amount;
    if (theAmount < 0) {
        out << '-';
        theAmount = -theAmount;
    }
    long dollars = theAmount / 100;
    out << '$' << dollars << '.';
    int cents = theAmount - dollars * 100;
    if (cents < 10) out << '0';
    out << cents;
}

ostream& operator<<(ostream& out,const Currency& x) {
    x.output(out);
    return out;
}

Currency Currency::operator+(const Currency &x) const {
    Currency result;
    result.amount = amount + x.amount;
    return result;
}

友元

可以赋予别的类和函数直接访问该类私有成员的权利,可将这些类和函数声明为该类的友元

friend ostream& operator<<(ostream&, const Currency&);

将ostream& operator<<声明为currency类的友元,就可以直接访问currency类的所有成员,这时就不用另外定义成员函数output

ostream& operator<<(ostream& out,const Currency& x) {
    long theAmount = x.amount;
    if (theAmount < 0) {
        out << '-';
        theAmount = -theAmount;
    }
    long dollars = theAmount / 100;
    out << '$' << dollars << '.';
    int cents = theAmount - dollars * 100;
    if (cents < 10) out << '0';
    out << cents;
    return out;
}