C++Primer练习[第七章]

//练习7.1:使用2.6.1节练习定义的Sales_data类为1.6节(第21页)的交易处理程序编写一个新版本。

/*

#include <iostream>

#include <string>

using std::string;

using std::cin;

using std::cout;

using std::endl;

using std::cerr;

struct Sales_data

{

    string bookNo;

    unsigned units_sold = 0;//销量

    double revenue = 0.0;//总售价

};

int main(int argc, const char * argv[])

{

    Sales_data total;

    double pjj = 0.0;

    if(std::cin >> total.bookNo >> total.units_sold >> total.revenue)

    {

        Sales_data trans;

        while(std::cin >> trans.bookNo >> trans.units_sold >> trans.revenue)

        {

            if(total.bookNo == trans.bookNo)

            {

                total.units_sold += trans.units_sold;

                total.revenue += trans.revenue;

            }

            else

            {

                if(total.units_sold != 0)

                {

                    pjj = total.revenue / total.units_sold;

                }

                else

                {

                     pjj = 0.0;

                }

                cout << total.bookNo <<"销售额为:" << total.revenue << "销量为: " << total.units_sold << "平均价为: " << pjj << endl;

                total.bookNo = trans.bookNo;

                total.units_sold = trans.units_sold;

                total.revenue = trans.revenue;

            }

        }

        if(total.units_sold != 0)

        {

            pjj = total.revenue / total.units_sold;

        }

        else

        {

            pjj = 0.0;

        }

        cout << total.bookNo <<"销售额为:" << total.revenue << "销量为: " << total.units_sold << "平均价为: " << pjj << endl;

    }

    else

    {

        cerr << "没有数据!" << endl;

        return -1;

    }

    system("pause");

    return 0;

}

*/



//练习7.2:曾在2.6.2节练习(第67页)中编写了一个Sales_data类,请向这个类添加combine和isbn成员

/*

#include <iostream>

#include <string>

using namespace std;

struct Sales_data

{

    std::string bookNo;             //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};

 */



//练习7.3:修改7.1.1节(第229页)的交易处理程序,令其使用这些成员。

/*

#include <iostream>

#include <string>

using std::string;

using std::cout;

using std::cin;

using std::endl;

using std::cerr;

struct Sales_data

{

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


int main(int argc, const char * argv[])

{

    Sales_data total;

    double pjj = 0.0;

    if(std::cin >> total.bookNo >> total.units_sold >> total.revenue)

    {

        Sales_data trans;

        while(std::cin >> trans.bookNo >> trans.units_sold >> trans.revenue)

        {

            if(total.isbn() == trans.isbn())

            {

                total.combine(trans);

            }

            else

            {

                if(total.units_sold != 0)

                {

                    pjj = total.revenue / total.units_sold;

                }

                else

                {

                    pjj = 0.0;

                }

                cout << total.bookNo <<"销售额为:" << total.revenue << "销量为: " << total.units_sold << "平均价为: " << pjj << endl;

                total.bookNo = trans.bookNo;

                total.units_sold = trans.units_sold;

                total.revenue = trans.revenue;

            }

        }

        if(total.units_sold != 0)

        {

            pjj = total.revenue / total.units_sold;

        }

        else

        {

            pjj = 0.0;

        }

        cout << total.bookNo <<"销售额为:" << total.revenue << "销量为: " << total.units_sold << "平均价为: " << pjj << endl;

    }

    else

    {

        cerr << "没有数据!" << endl;

        return -1;

    }

    system("pause");

    return 0;

}

*/


//练习7.4:编写一个名为Person的类,使其表示人员的姓名和地址。使用string对象存放这些元素,接下来的练习将不断充实这个类的其他特征

/*

#include <string>

using std::string;

class Person

{

public:

    string name;

    string address;

};

*/


//练习7.5:在你的Person类中提供一些操作使其能够返回姓名和住址。这些函数是否应该是const的呢?解释原因。

//答:应该是const。因为只需要返回成员变量的值不需要修改它,定义成const有助于提高函数的灵活性。

/*

#include <string>

using std::string;

class Person

{

private:

    string name;

    string address;

    

public:

    string GetName()const

    {

        return name;

    }

    string GetAddress()const

    {

        return address;

    }

};

*/


//练习7.6:对于add, read和print,定义你自己的版本

/*

#include <iostream>

#include <string>

using std::istream;

using std::ostream;

using std::string;

using std::cout;

using std::cin;

using std::endl;

struct Sales_data

{

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


Sales_data add(Sales_data& _data1, Sales_data& _data2)

{

    Sales_data sum = _data1;

    return sum.combine(_data2);

}


istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}


ostream& print(ostream &os, const Sales_data&item)

{

    os << "isbn = " << item.isbn()

        << "销量 = " << item.units_sold

        << "总价 = " << item.revenue

        << "平均单价 = " << item.price;

    return os;

}

*/


//练习7.7:使用这些新函数重写7.1.2节(第233页)练习中的交易处理程序。

/*

#include <iostream>

#include <string>

using std::istream;

using std::ostream;

using std::string;

using std::cout;

using std::cin;

using std::endl;

using std::cerr;

struct Sales_data

{

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


Sales_data add(Sales_data& _data1, Sales_data& _data2)

{

    Sales_data sum = _data1;

    return sum.combine(_data2);

}


istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}


ostream& print(ostream &os, const Sales_data&item)

{

    os << "isbn = " << item.isbn()

    << "销量 = " << item.units_sold

    << "总价 = " << item.revenue

    << "平均单价 = " << item.price;

    return os;

}


int main(int argc, const char * argv[])

{

    Sales_data total;

    double pjj = 0.0;

    if(read(cin, total))

    {

        Sales_data trans;

        while(read(cin, trans))

        {

            if(total.isbn() == trans.isbn())

            {

                total.combine(trans);

            }

            else

            {

                if(total.units_sold != 0)

                {

                    pjj = total.revenue / total.units_sold;

                }

                else

                {

                    pjj = 0.0;

                }

                print(cout, total) << endl;

                total.bookNo = trans.bookNo;

                total.units_sold = trans.units_sold;

                total.revenue = trans.revenue;

            }

        }

        if(total.units_sold != 0)

        {

            pjj = total.revenue / total.units_sold;

        }

        else

        {

            pjj = 0.0;

        }

        print(cout, total) << endl;

    }

    else

    {

        cerr << "没有数据!" << endl;

        return -1;

    }

    system("pause");

    return 0;

}

*/



//练习7.8:为什么read函数将其Sales_data参数定义为普通的引用,而print将其参数定义成常量引用?

//答:因为read需要修改到Sales_data参数的成员变量,而print不需要修改到。


//练习7.9:对于7.1.2节(第233页)练习中的代码,添加读取和打印Person对象的操作。

/*

#include <string>

#include <iostream>

using std::istream;

using std::ostream;

using std::string;

using std::cin;

using std::cout;

using std::endl;

class Person

{

public:

    string name;

    string address;

    

public:

    string GetName()const

    {

        return name;

    }

    string GetAddress()const

    {

        return address;

    }

    

};


istream &read(istream&is, Person &per)

{

    is >> per.name >> per.address;

    return is;

}


ostream& print(ostream &os, const Person &per)

{

    os << "name = " << per.GetName()

        <<"Address = " << per.GetAddress();

    return os;

}

*/


//练习7.10:在下面if语句中,条件部分的作用是什么?

//    if(read(read(cin,data1),data2)

//答:输入data1,data2,如果输入data2遇到结束符则条件判断为false,否则为true


//练习7.11:在你的Sales_data类中添加构造函数,然后编写一段程序令其用到每个构造函数

/*

#include <iostream>

#include <string>

using std::istream;

using std::ostream;

using std::string;

using std::cout;

using std::cin;

using std::endl;

using std::cerr;

struct Sales_data

{

    Sales_data() = default;

    Sales_data(istream&);

    Sales_data(const string &s) : bookNo(s){}

    Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n){}

    

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


Sales_data add(Sales_data& _data1, Sales_data& _data2)

{

    Sales_data sum = _data1;

    return sum.combine(_data2);

}


istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}

ostream& print(ostream &os, const Sales_data&item)

{

    os << "isbn = " << item.isbn()

    << "销量 = " << item.units_sold

    << "总价 = " << item.revenue

    << "平均单价 = " << item.price;

    return os;

}

Sales_data::Sales_data(istream& is)

{

    read(is, *this);

}

int main(int argc, const char * argv[])

{

    Sales_data s1;

    Sales_data s2(cin);

    Sales_data s3("bookNo");

    Sales_data s4("bookNo", 10, 5);

    cout << "s1 : ";

    print(cout, s1) << endl;

    cout << "s2 : ";

    print(cout, s2) << endl;

    cout << "s3 : ";

    print(cout, s3) << endl;

    cout << "s4 : ";

    print(cout, s4) << endl;

    system("pause");

    return 0;

}

*/



//练习7.12:把只接受一个istream作为参数的构造函数定义到类的内部。

/*

#include <iostream>

#include <string>

using std::istream;

using std::string;

using std::cin;

struct Sales_data;

istream& read(istream &is, Sales_data&item);

struct Sales_data

{

    Sales_data() = default;

    Sales_data(istream& is)

    {

        read(is, *this);

    }

    Sales_data(const string &s) : bookNo(s){}

    Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n){}

    

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};

istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}

*/


//练习7.13:使用istream构造函数重写第229页的程序

/*

#include <iostream>

#include <string>

using std::istream;

using std::ostream;

using std::string;

using std::cout;

using std::cin;

using std::endl;

using std::cerr;

struct Sales_data;

istream& read(istream &is, Sales_data&item);

struct Sales_data

{

    Sales_data() = default;

    Sales_data(istream& is)

    {

        read(is, *this);

    }

    Sales_data(const string &s) : bookNo(s){}

    Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n){}

    

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


Sales_data add(Sales_data& _data1, Sales_data& _data2)

{

    Sales_data sum = _data1;

    return sum.combine(_data2);

}


istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}

ostream& print(ostream &os, const Sales_data&item)

{

    os << "isbn = " << item.isbn()

    << "销量 = " << item.units_sold

    << "总价 = " << item.revenue

    << "平均单价 = " << item.price;

    return os;

}

int main(int argc, const char * argv[])

{

    Sales_data total(cin);

    double pjj = 0.0;

    if(total.isbn() != "-1" )

    {

        Sales_data trans;

        while(read(cin, trans))

        {

            if(total.isbn() == trans.isbn())

            {

                total.combine(trans);

            }

            else

            {

                if(total.units_sold != 0)

                {

                    pjj = total.revenue / total.units_sold;

                }

                else

                {

                    pjj = 0.0;

                }

                print(cout, total) << endl;

                total.bookNo = trans.bookNo;

                total.units_sold = trans.units_sold;

                total.revenue = trans.revenue;

            }

        }

        if(total.units_sold != 0)

        {

            pjj = total.revenue / total.units_sold;

        }

        else

        {

            pjj = 0.0;

        }

        print(cout, total) << endl;

    }

    else

    {

        cerr << "没有数据!" << endl;

        return -1;

    }

    system("pause");

    return 0;

}

*/



//练习7.14:编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员

/*

#include <iostream>

#include <string>

using std::istream;

using std::string;

using std::cin;

struct Sales_data;

istream& read(istream &is, Sales_data&item);

struct Sales_data

{

    Sales_data() : bookNo(""),units_sold(0),revenue(0.0){};

    Sales_data(istream& is) : bookNo(""),units_sold(0),revenue(0.0)

    {

        read(is, *this);

    }

    Sales_data(const string &s)  : bookNo(s),units_sold(0),revenue(0.0){}

    Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n){}

    

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};

istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}

*/


//练习7.15:为你的Person类添加正确的构造函数。

/*

#include <string>

#include <iostream>

using std::istream;

using std::ostream;

using std::string;

using std::cin;

using std::cout;

using std::endl;

struct Person

{

    Person() = default;

    Person(const string& _name, const string & _address) :name(_name),address(_address){}

    Person(const string& _name) :name(_name){}

    string name;

    string address;

    

    string GetName()const

    {

        return name;

    }

    string GetAddress()const

    {

        return address;

    }

    

};


istream &read(istream&is, Person &per)

{

    is >> per.name >> per.address;

    return is;

}


ostream& print(ostream &os, const Person &per)

{

    os << "name = " << per.GetName()

    <<"Address = " << per.GetAddress();

    return os;

}

*/


//练习7.16:在类的定义中对于访问说明符出现的位置和次数有限制吗?如果有,是什么?什么样的成员应该定义在public说明符之后?什么样的成员应该定义在private说明符之后?

//答:没有限制。在整个程序内可被访问的chengy(如类的接口)应该定义在public说明符之后,不能被使用该类的代码访问的成员应该定义在private说明符之后。


//练习7.17:使用class和struct时有区别吗?如果有,是什么?

//答:使用class和struct定义类的唯一区别就是默认的访问权限。class定义在第一个访问说明符之前的成员是private的,struct定义在第一个访问说明符之前的成员是public的.


//练习7.18:封装是何含义?它有什么用处?

//答:封装即隐藏类的实现细节。用处是1.确保用户代码不会无意间破坏封装对象的状态 2.被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。


//练习7.19:在你的Person类中,你将把哪些成员声明成public的?哪些声明成private的?解释你这样做的原因

//答:封装成员name address。确保用户代码不会无意间破坏封装对象的状态

/*

class Person

{

private:

    string name;

    string address;

    

public:

    Person() = default;

    Person(const string& _name, const string & _address) :name(_name),address(_address){}

    Person(const string& _name) :name(_name){}

    string GetName()const

    {

        return name;

    }

    string GetAddress()const

    {

        return address;

    }

};

 */


//练习7.20:友元什么时候有用?请分别列举出使用友元的利弊。

//答:在其他类或者函数需要访问某个类的非公有成员的时候有用。利:可以让别的类或函数访问类的私有数据成员。弊:数据成员的安全性可能会降低。


//练习7.21:修改你的Sales_data类使其隐藏实现的细节。你之前编写的关于Sales_data操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其工作正常

/*

#include <iostream>

#include <string>

using std::istream;

using std::ostream;

using std::string;

using std::cout;

using std::cin;

using std::endl;

using std::cerr;

class Sales_data;

istream& read(istream &is, Sales_data&item);

class Sales_data

{

    friend istream& read(istream &is, Sales_data&item);

    friend ostream& print(ostream &os, const Sales_data&item);

    friend int main(int argc, const char * argv[]);

private:

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

public:

    Sales_data() = default;

    Sales_data(istream& is)

    {

        read(is, *this);

    }

    Sales_data(const string &s) : bookNo(s){}

    Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n){}

    Sales_data& combine(Sales_data& _data)

    {

        units_sold += _data.units_sold;

        revenue += _data.revenue;

        if(units_sold != 0)

        {

            price =revenue/units_sold;

        }

        return *this;

    }

    

    string isbn()const

    {

        return bookNo;

    }

};


Sales_data add(Sales_data& _data1, Sales_data& _data2)

{

    Sales_data sum = _data1;

    return sum.combine(_data2);

}


istream& read(istream &is, Sales_data&item)

{

    double _price = 0.0;

    is >> item.bookNo >> item.units_sold >> _price;

    item.revenue = item.units_sold *_price;

    if(item.units_sold != 0)

    {

        item.price =item.revenue/item.units_sold;

    }

    return is;

}

ostream& print(ostream &os, const Sales_data&item)

{

    os << "isbn = " << item.isbn()

    << "销量 = " << item.units_sold

    << "总价 = " << item.revenue

    << "平均单价 = " << item.price;

    return os;

}

int main(int argc, const char * argv[])

{

    Sales_data total(cin);

    double pjj = 0.0;

    if(total.isbn() != "-1" )

    {

        Sales_data trans;

        while(read(cin, trans))

        {

            if(total.isbn() == trans.isbn())

            {

                total.combine(trans);

            }

            else

            {

                if(total.units_sold != 0)

                {

                    pjj = total.revenue / total.units_sold;

                }

                else

                {

                    pjj = 0.0;

                }

                print(cout, total) << endl;

                total.bookNo = trans.bookNo;

                total.units_sold = trans.units_sold;

                total.revenue = trans.revenue;

            }

        }

        if(total.units_sold != 0)

        {

            pjj = total.revenue / total.units_sold;

        }

        else

        {

            pjj = 0.0;

        }

        print(cout, total) << endl;

    }

    else

    {

        cerr << "没有数据!" << endl;

        return -1;

    }

    system("pause");

    return 0;

}

*/


//练习7.22:修改你的Person类使其隐藏实现的细节

/*

class Person

{

    friend istream &read(istream&is, Person &per);

    friend ostream& print(ostream &os, const Person &per);

private:

    string name;

    string address;

    

public:

    Person() = default;

    Person(const string& _name, const string & _address) :name(_name),address(_address){}

    Person(const string& _name) :name(_name){}

    string GetName()const

    {

        return name;

    }

    string GetAddress()const

    {

        return address;

    }

};


istream &read(istream&is, Person &per)

{

    is >> per.name >> per.address;

    return is;

}


ostream& print(ostream &os, const Person &per)

{

    os << "name = " << per.GetName()

    <<"Address = " << per.GetAddress();

    return os;

}

*/


//练习7.23:编写你自己的Screen类

/*

#include <string>

using std::string;


class Screen

{

public:

    using pos = string::size_type;


private:

    pos cursor = 0;

    pos height = 0;

    pos width = 0;

    string contents;

    mutable size_t access_ctr = 0;

    

public:

    Screen() = default;

    Screen(pos ht, pos wd, char c);

    char get() const;

    char get(pos r, pos c) const;

    Screen &move(pos r, pos c);

    void some_member() const;

};


Screen::Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)

{

    

}


inline char Screen::get() const

{

    return contents[cursor];

}


inline char Screen::get(pos r, pos c) const

{

    pos row = r * width;

    return contents[row + c];

}


inline Screen & Screen::move(pos r, pos c)

{

    pos row = r * width;

    cursor = row + c;

    return *this;

}


inline void Screen::some_member() const

{

    ++access_ctr;

}

*/


//练习7.24:给你的Screen类添加三个构造函数:一个默认的构造函数;另一个构造函数接受宽和高的值,然后将contents初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化之后屏幕的内容。

/*

#include <string>

using std::string;


class Screen

{

public:

    using pos = string::size_type;

    

private:

    pos cursor = 0;

    pos height = 0;

    pos width = 0;

    string contents;

    mutable size_t access_ctr = 0;

    

public:

    Screen() = default;

    Screen(pos ht, pos wd);

    Screen(pos ht, pos wd, char c);

    char get() const;

    char get(pos r, pos c) const;

    Screen &move(pos r, pos c);

    void some_member() const;

};


Screen::Screen(pos ht, pos wd): height(ht), width(wd), contents(ht * wd, ' ')

{

    

}


Screen::Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)

{

    

}


inline char Screen::get() const

{

    return contents[cursor];

}


inline char Screen::get(pos r, pos c) const

{

    pos row = r * width;

    return contents[row + c];

}


inline Screen & Screen::move(pos r, pos c)

{

    pos row = r * width;

    cursor = row + c;

    return *this;

}


inline void Screen::some_member() const

{

    ++access_ctr;

}

*/


//练习7.25:Screen能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能,为什么?

//答:能。因为编译器能替我们合成拷贝、赋值和销毁的操作。Screen的成员变量都可以使用默认的拷贝、赋值操作。


//练习7.26:将Sales_data::avg_price定义成内联函数。

/*

class Sales_data

{

    friend istream& read(istream &is, Sales_data&item);

    friend ostream& print(ostream &os, const Sales_data&item);

    friend int main(int argc, const char * argv[]);

private:

    string bookNo;                  //isbn

    unsigned units_sold = 0;        //销量

    double revenue = 0;             //总价

    double price = 0.0f;            //单价

    

public:

    //...

    inline double avg_price()

    {

       return units_sold == 0? 0 : revenue/units_sold;

    }

    //...

};

*/


//练习7.27:给你自己的Screen类添加move,set和display函数,通过执行下面的代码检验你的类是否正确。

//Screen myScreen(5, 5, ’X’);

//myScreen.move(4,0).set(‘#’).display(cout);

//cout << “\n”;

//myScreen.display(cout);

//cout << “\n”;

/*

#include <iostream>

#include <string>

using std::string;

using std::ostream;

using std::cout;

class Screen

{

public:

    using pos = string::size_type;

    

private:

    pos cursor = 0;

    pos height = 0;

    pos width = 0;

    string contents;

    mutable size_t access_ctr = 0;

    

public:

    Screen() = default;

    Screen(pos ht, pos wd);

    Screen(pos ht, pos wd, char c);

    char get() const;

    char get(pos r, pos c) const;

    Screen &move(pos r, pos c);

    void some_member() const;

    Screen &Set(char c);

    Screen &Set(pos r, pos col, char ch);

    Screen &display(ostream &os);

    const Screen &display(ostream &os) const;

    

private:

    void do_display(ostream &os) const

    {

        os << contents;

    }

};


Screen::Screen(pos ht, pos wd): height(ht), width(wd), contents(ht * wd, ' ')

{

    

}


Screen::Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)

{

    

}


inline char Screen::get() const

{

    return contents[cursor];

}


inline char Screen::get(pos r, pos c) const

{

    pos row = r * width;

    return contents[row + c];

}


inline Screen & Screen::move(pos r, pos c)

{

    pos row = r * width;

    cursor = row + c;

    return *this;

}


inline void Screen::some_member() const

{

    ++access_ctr;

}


inline Screen &Screen::Set(char c)

{

    contents[cursor] = c;

    return *this;

}


inline Screen &Screen::Set(pos r, pos col, char ch)

{

    contents[r*width + col] = ch;

    return *this;

}


inline Screen &Screen::display(ostream &os)

{

    do_display(os);

    return *this;

}


inline const Screen &Screen::display(ostream &os) const

{

    do_display(os);

    return *this;

}


int main()

{

    Screen myScreen(5, 5, 'X');

    myScreen.move(4,0).Set('#').display(cout);

    cout << "\n";

    myScreen.display(cout);

    cout << "\n";

    system("pause");

    return 0;

}

*/



//练习7.28:如果move,set和display函数的返回类型不是Screen&而是Screen,则在上一个练习中将会发生什么情况?

//答:行为将大不相同。返回值是*this的副本,调用set只能改变临时副本,而不能改变myScreen的值。


//练习7.29:修改你的Screen类,令move,set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?

/*

#include <iostream>

#include <string>

using std::string;

using std::ostream;

using std::cout;

class Screen

{

public:

    using pos = string::size_type;

    

private:

    pos cursor = 0;

    pos height = 0;

    pos width = 0;

    string contents;

    mutable size_t access_ctr = 0;

    

public:

    Screen() = default;

    Screen(pos ht, pos wd);

    Screen(pos ht, pos wd, char c);

    char get() const;

    char get(pos r, pos c) const;

    Screen move(pos r, pos c);

    void some_member() const;

    Screen Set(char c);

    Screen Set(pos r, pos col, char ch);

    Screen display(ostream &os);

    const Screen display(ostream &os) const;

    

private:

    void do_display(ostream &os) const

    {

        os << contents;

    }

};


Screen::Screen(pos ht, pos wd): height(ht), width(wd), contents(ht * wd, ' ')

{

    

}


Screen::Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)

{

    

}


inline char Screen::get() const

{

    return contents[cursor];

}


inline char Screen::get(pos r, pos c) const

{

    pos row = r * width;

    return contents[row + c];

}


inline Screen Screen::move(pos r, pos c)

{

    pos row = r * width;

    cursor = row + c;

    return *this;

}


inline void Screen::some_member() const

{

    ++access_ctr;

}


inline Screen Screen::Set(char c)

{

    contents[cursor] = c;

    return *this;

}


inline Screen Screen::Set(pos r, pos col, char ch)

{

    contents[r*width + col] = ch;

    return *this;

}


inline Screen Screen::display(ostream &os)

{

    do_display(os);

    return *this;

}


inline const Screen Screen::display(ostream &os) const

{

    do_display(os);

    return *this;

}


int main()

{

    Screen myScreen(5, 5, 'X');

    myScreen.move(4,0).Set('#').display(cout);

    cout << "\n";

    myScreen.display(cout);

    cout << "\n";

    system("pause");

    return 0;

}

*/



//练习7.30:通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示地使用指针访问成员的优缺点。

//答:当形参名字与成员变量相同时,可用this指针显式指明是用成员变量。缺点是大部分时候是没必要的,比较麻烦。


//练习7.31:定义一对类X和Y,其中X包含一个指向Y的指针,而Y包含一个类型为X的对象

/*

class Y;

class X

{

    Y* p;

};

class Y

{

    X val;

};

*/


//练习7.32:定义你自己的Screen和Window_mgr,其中clear是window_mgr的成员,是Screen的友元

/*

#include <iostream>

#include <string>

#include <vector>

using std::string;

using std::ostream;

using std::cout;

using std::vector;

class Screen;

class Window_mgr

{

public:

    Window_mgr();

    using ScreenIndex = vector<Screen>::size_type;

    void clear(ScreenIndex i);

private:

    vector<Screen> screens;

};

class Screen

{

public:

    friend void Window_mgr::clear(ScreenIndex i);

    

public:

    using pos = string::size_type;

    

private:

    pos cursor = 0;

    pos height = 0;

    pos width = 0;

    string contents;

    mutable size_t access_ctr = 0;

    

public:

    Screen() = default;

    Screen(pos ht, pos wd);

    Screen(pos ht, pos wd, char c);

    char get() const;

    char get(pos r, pos c) const;

    Screen &move(pos r, pos c);

    void some_member() const;

    Screen &Set(char c);

    Screen &Set(pos r, pos col, char ch);

    Screen &display(ostream &os);

    const Screen &display(ostream &os) const;

    

private:

    void do_display(ostream &os) const

    {

        os << contents;

    }

};


Screen::Screen(pos ht, pos wd): height(ht), width(wd), contents(ht * wd, ' ')

{

    

}


Screen::Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)

{

    

}


inline char Screen::get() const

{

    return contents[cursor];

}


inline char Screen::get(pos r, pos c) const

{

    pos row = r * width;

    return contents[row + c];

}


inline Screen & Screen::move(pos r, pos c)

{

    pos row = r * width;

    cursor = row + c;

    return *this;

}


inline void Screen::some_member() const

{

    ++access_ctr;

}


inline Screen &Screen::Set(char c)

{

    contents[cursor] = c;

    return *this;

}


inline Screen &Screen::Set(pos r, pos col, char ch)

{

    contents[r*width + col] = ch;

    return *this;

}


inline Screen &Screen::display(ostream &os)

{

    do_display(os);

    return *this;

}


inline const Screen &Screen::display(ostream &os) const

{

    do_display(os);

    return *this;

}

Window_mgr::Window_mgr():screens({ Screen(24, 80, ' ')})

{

    

}


void Window_mgr::clear(ScreenIndex i)

{

    Screen &s = screens[i];

    s.contents = string(s.height * s.width, ' ');

}

*/


//练习7.33:如果我们给Screen添加一个如下所示的size成员将发生什么情况?如果出现了问题,请尝试修改它。

//pos Screen::size() const

//{

//    return height * width;

//}

//答:编译不过,因为pos在Screen的作用域外。修改成:

//Screen::pos Screen::size() const

//{

//    return height * width;

//}


//练习7.34:如果我们把第256页Screen类的pos的typedef放在类的最后一行会发生什么情况?

//答:编译不过,pos没有被声明。用来定义类型的成员必须先定义后使用。声明中使用的名字,包括返回类型或者参数列表中使用的名字,都必须在使用前确保可见。如果某个成员的生命使用了类中尚未出现的名字,则编译器将会在定义该类的作用于中继续查找。编译器只考虑就在使用类型之前出现的声明。


//练习7.35:解释下面代码的含义,说明其中的Type和initVal分别使用了哪个定义。如果代码存在错误,尝试修改它。

//typedef string Type;

//Type initVal();

//class Exercise{

//public:

//    typedef double Type;

//    Type setVal(Type);

//    Type initVal();

//private:

//    int val;

//};

//Type Exercise::setVal(Type parm){

//    val = parm + initVal();

//    return val;

//}

//答:Type initVal();使用了typedef string Type;

//Type setVal(Type);Type使用了typedef double Type;Type initVal();Type使用了typedef double Type;

//Type Exercise::setVal(Type parm)返回类型Type用的typedef string Type;与声明不对应,会报错

//val = parm + initVal();的parm用得是typedef double Type;initVal();用得是class Exercise的成员函数Type initVal();

//Type initVal();都未定义。

//改造如下:

//typedef string Type;

//Type initVal(){return "";}

//class Exercise{

//public:

//    typedef double Type;

//    Type setVal(Type);

//    Type initVal(){return 0;}

//private:

//    int val;

//};

//Exercise::Type Exercise::setVal(Type parm){

//    val = parm + initVal();

//    return val;

//}


//练习7.36:下面的初始值是错误的,请找出问题所在,并尝试修改它

//Struct X{

//    X(int i, int j) : base(i), rem(base%j){}

//    int rem,base;

//};

//答:成员的初始化顺序与它们在类定义中的出现顺序一致。rem先被初始化,因此这个初始值的效果是试图使用未定义的值base%j来初始化rem.

//改为:

//Struct X{

//    X(int i, int j) : base(i), rem(i%j){}

//    int rem,base;

//};


//练习7.37:使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有数据成员的值。

//Sales_data first_item(cin);

//int main()

//{

//    Sales_data next;

//    Sales_data last(“9-999-99999-9”);

//}

//答:first_item的构造函数:Sales_data(std::istream &is){read(is, *this);}

//bookNo = "", units_sold 未定义, revenue 未定义

//next的构造函数: Sales_data(std::string s = "") : bookNo(s){}

//bookNo = "", units_sold 未定义, revenue 未定义

//last的构造函数: Sales_data(std::string s = "") : bookNo(s){}

//bookNo = "9-999-99999-9", units_sold 未定义, revenue 未定义


//练习7.38:有些情况下我们希望提供cin作为接受istream&参数的构造函数的默认实参,请声明这样的构造函数。

//Sales_data(std::istream &is = std::cin){read(is, *this);}


//练习7.39:如果接受string的构造函数和接受istream&的构造函数都使用默认实参,这种行为合法吗?如果不,为什么?

//答:不合法。因为如果定义Sales_datae的时候不带参数,就不知道要选哪个构造函数了,有二义性。


//练习7.40:从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。

//(a)Book       (b)Date     (c)Employee

//(d)Vehicle    (e)Object   (f)Tree

//答:(a)Book

//class Book

//{

//public:

//  Book() = default;

//  Book(string _name) : name(_name){}

//private:

//  string name;

//}


//练习7.41:使用委托构造函数重新编写你的Sales_data类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的方式分别创建Sales_data对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。

/*

 #include <iostream>

 #include <string>

 using std::istream;

 using std::ostream;

 using std::string;

 using std::cout;

 using std::cin;

 using std::endl;

 using std::cerr;

 class Sales_data;

 istream& read(istream &is, Sales_data&item);

 class Sales_data

 {

 friend istream& read(istream &is, Sales_data&item);

 friend ostream& print(ostream &os, const Sales_data&item);

 friend int main(int argc, const char * argv[]);

 private:

 string bookNo;                  //isbn

 unsigned units_sold = 0;        //销量

 double revenue = 0;             //总价

 double price = 0.0f;            //单价

 

 public:

 Sales_data(): Sales_data("", 0, 0)

 {

     cout << "Sales_data()" << endl;

 }

 Sales_data(istream& is) : Sales_data("", 0, 0)

 {

     read(is, *this);

     cout << "Sales_data(istream& is)" << endl;

 }

 Sales_data(const string &s) : Sales_data(s, 0, 0)

 {

     cout << "Sales_data(const string &s)" << endl;

 }

 Sales_data(const string &s, unsigned n, double p) :bookNo(s),units_sold(n),revenue(p*n)

 {

     cout << "Sales_data(const string &s, unsigned n, double p)" << endl;

     

 }

     

 Sales_data& combine(Sales_data& _data)

 {

 units_sold += _data.units_sold;

 revenue += _data.revenue;

 if(units_sold != 0)

 {

 price =revenue/units_sold;

 }

 return *this;

 }

 

 string isbn()const

 {

 return bookNo;

 }

 };

 

 Sales_data add(Sales_data& _data1, Sales_data& _data2)

 {

 Sales_data sum = _data1;

 return sum.combine(_data2);

 }

 

 istream& read(istream &is, Sales_data&item)

 {

 double _price = 0.0;

 is >> item.bookNo >> item.units_sold >> _price;

 item.revenue = item.units_sold *_price;

 if(item.units_sold != 0)

 {

 item.price =item.revenue/item.units_sold;

 }

 return is;

 }

 ostream& print(ostream &os, const Sales_data&item)

 {

     os << "isbn = " << item.isbn()

     << "销量 = " << item.units_sold

     << "总价 = " << item.revenue

     << "平均单价 = " << item.price;

     return os;

 }

 int main(int argc, const char * argv[])

 {

     Sales_data a;

     Sales_data b(cin);

     Sales_data c("bookNo");

     Sales_data d("bookNo", 1, 2);

     system("pause");

     return 0;

 }

 */



//练习7.42:对于在练习7.40(参见7.5.1节,第261页)中编写的类,确定哪些构造函数可以使用委托,如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。

//答:

//class Book

//{

//public:

//  Book() : Book(""){};

//  Book(string _name) : name(_name){}

//private:

//  string name;

//}


//练习7.43;假定有一个名为NoDefault的类,它有一个接受int的构造函数,但是没有默认构造函数。定义类C,C有一个NoDefault类型的成员,定义C的默认构造函数。

/*

#include <iostream>

class NoDefault

{

public:

    NoDefault(int i) : ci(i){ std::cout << "NoDefault(int i), ci = " << ci << std::endl;}

    

    int ci = 0;

};

class C

{

public:

    C() : val(1){std::cout << "C()" << std::endl;}

    NoDefault val;

};

int main()

{

    C test;

    return 0;

}

 */



//练习7.44:下面这条声明合法吗?如果不,为什么?

//vector<NoDefault> vec(10);

//答:不合法。NoDefault没有默认构造函数。当对象被默认初始化或值初始化时自动执行默认构造函数。其中,值初始化在当我们通过书写形如T()的表达式显示地请求值初始化时,其中T时类型名(vector的一个构造函数只接受一个实参用于说明vector的大小,它就是使用一个这种形式的实参来对它的元素初始化器进行值初始化).


//练习7.45:如果在一个练习中定义的vector的元素类型是C,则声明合法吗?为什么?

//答:合法。因为C有默认构造函数。

/*

#include <iostream>

#include <vector>

class NoDefault

{

public:

    NoDefault(int i) : ci(i){ std::cout << "NoDefault(int i), ci = " << ci << std::endl;}

    

    int ci = 0;

};

class C

{

public:

    C() : val(1){std::cout << "C()" << std::endl;}

    NoDefault val;

};

int main()

{

    std::vector<C> vec(10);

    return 0;

}

 */



//练习7.46:下面哪些论断是不正确的?为什么?

//(a) 一个类必须至少提供一个构造函数

//(b) 默认构造函数是参数列表为空的构造函数。

//(c) 如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数

//(d) 如果类没有定义默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。

//答:(a)正确。

//(b)正确。

//(c)不正确。因为当对象被默认初始化或值初始化时自动执行默认构造函数。如果没有默认构造函数则编译会报错。所以就算不存在有意义的默认值也应该有默认构造函数。

//(d)不正确。编译器会生成一个默认构造函数,如果存在类内初始值,用它来初始化成员。否则默认初始化该成员。但是如果定义在块中的内置类型或复合类型的对象被默认初始化,则他们的值将是未定义的,没有默认值。


//练习7.47:说明接受一个string参数的Sales_data构造函数是否应该是explicit的,并解释这样做的优缺点。

//答:是否需要从string到Sales_data的转换依赖于我们对用户使用该转换的看法。在此例中,这种转换可能是对的。使用explicit的优点是比较严谨,必须显示地使用构造函数。缺点是不灵活。


//练习7.48:假定Sales_data的构造函数不是explicit的,则下述定义将执行什么样的操作?

//string null_isbn(“9-999-99999-9”);

//Sales_data item1(null_isbn);

//Sales_data item2(“9-999-99999-9”);

//如果Sales_data的构造函数是explicit的,又会发生什么呢?

//答:不是explicit的:

//string null(“9-999-99999-9”);定义一个名为null的string变量,直接初始化为"9-999-99999-9"

//Sales_data item1(null_isbn);调用Sales_data的构造函数 Sales_data(const std::string &s);构建一个名为item1的Sales_data对象。

//Sales_data item2(“9-999-99999-9”);把“9-999-99999-9”转换为string,调用Sales_data的构造函数 Sales_data(const std::string &s);构建一个名为item1的Sales_data对象。

//是explicit的:

//与不是explicit一样。因为这个是直接调用构造函数,并没有进行隐式的类类型转换。


//练习7.49:对于combine函数的三种不同声明,当我们调用i.combine(s)时分别发生什么情况?其中i是一个Sales_data,而s是一个string对象。

//(a) Sales_data &combine(Sales_data);

//(b) Sales_data &combine(Sales_data&);

//(c) Sales_data &combine(const Sales_data&) const;

//答:(a)s隐式转换为Sales_data,形成一个临时变量,用这个Sales_data构造一个临时变量。

//(b)s隐式转换为Sales_data,形成一个临时变量,用这个Sales_data初始化引用,临时变量的值会被可变

//(c)s隐式转换为Sales_data,形成一个临时变量,用这个Sales_data初始化const引用,临时变量的值不会被改变。


//练习7.50:确定你的Person类中是否有一些构造函数应该是explicit的。

//答:不需要


//练习7.51:vector将其参数的构造函数定义成explicit的,而string则不是,你觉得原因何在?

//答:vector是一个容器,必须指定容器里装的对象类型,如vector<char>是一个新的类型,如果通过char类型转换为vector<char>,则vector的灵活性受到影响,具体来说,vector<char>可以push_back是char的类型,vector各个函数的调用很方便,如果通过类类型转换,反而使得vector的使用受限,string类型不是explicit,string类型可以自然接收不同类类型的转换,支持隐式转换使得跟字符串的处理变得灵活。


//练习7.52:使用2.6.1节(第64页)的Sales_data类,解释下面的初始化初始化过程,如果存在问题,尝试修改它。

//Sales_data item = {“978-0590353403”,25,15.99};

//答:我们可以提供一个花括号括起来的成员初始值列表,并用它初始化聚合类的数据成员,但是

//struct Sales_data{

//std::string bookNo;

//unsigned units_sold = 0;

//double revenue = 0.0;

//};

//不是聚合类,因为units_sold和revenue有类内初始值,需要把他修改成聚合类:

//struct Sales_data{

//std::string bookNo;

//unsigned units_sold;

//double revenue;

//};

//即用“978-0590353403”初始化bookNo,25初始化units_sold,15.99初始化revenue


//练习7.53:定义你自己的Debug。

/*

#include <iostream>

#include <string>

#include <vector>


using namespace std;


class Debug{

public:

    constexpr Debug(bool b = true) : hw(b), io(b), other(b) { }

    constexpr Debug(bool h, bool i, bool o) : hw(h), io(i), other(o) { }

    constexpr bool any() { return hw || io || other; }

    void set_io(bool b) { io = b;}

    void set_hw(bool b) { hw = b;}

    void set_other(bool b) { hw = b;}

private:

    bool hw;

    bool io;

    bool other;

    

};


int main()

{

    Debug io_sub(false, true, false);

    if(io_sub.any())

        cerr << "print appropriate error messages" << endl;

    Debug prod(false);

    if(prod.any())

        cerr << "print an error message" << endl;

    

    return 0;

}

 */


//练习7.54:Debug中以set_开头的成员应该被声明成constexpr吗?如果不,为什么?

//答:不应该,因为set_需要将需要的数据成员设置成别的值,因此,不能声明成constexpr的


//练习7.55:7.5.5节(第266页)的Data类是字面值常量吗?请解释原因。

//答:不是,因为string不是一个字面值常量类。


//练习7.56:什么是类的静态成员?它有何优点?静态成员与普通成员有何区别?

//答:类的静态成员和类本身直接相关,而不是与类的各个对象保持关联.

//静态成员独立于任何对象.因此,在某些非静态数据成员可能非法的场合,静态成员却可以正常地使用.静态数据成员的类型可以就是它所属的类类型,而非静态数据成员则受到限制,只能声明成它所属类的指针或引用.


//练习7.57:编写你自己的Account类

/*

#include <iostream>

#include <vector>


using namespace std;


class Account{

public:

    void calculate() { amount += amount * interestRate;}

    static double rate() {

        return interestRate;

    }

    static void rate(double);

private:

    std::string owner;

    double amount;

    static double interestRate;

    static double initRate();

    

};


void Account::rate(double newRate)

{

    interestRate = newRate;

}


double Account::initRate()

{

    return 0.02;

}

double Account::interestRate = initRate();

*/


//练习7.58:下面的静态数据成员的声明和定义有错误吗?请解释原因

/*

//example.h

class Example{

public:

    static double rate = 6.5;

    static const int vecSize = 20;

    static vector<double> vec(vecSize);

};


//example.C

#include “example.h”

double Example::rate;

vector<double> Example::vec;

*/

//答:通常情况下,类的静态成员不应该在类的内部初始化.然而,我们可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是字面值常量类型的constexpr.初始值必须是常量表达式,因为这些成员本身就是常量表达式,所以他们能用在所有适合于常量表达式的地方.

// static double rate = 6.5; 应改成 static double rate;

//static const int vecSize = 20; 应改为 static constexpr int vecSize = 20;

//static vector<double> vec(vecSize);应改为 static vector<double> vec;

//double Example::rate;应改为double Example::rate = 6.5;

//vector<double> Example::vec;应改为 vector<double> Example::vec(vecSize);


猜你喜欢

转载自blog.csdn.net/sukeychen/article/details/81054818