【8-25】C++ Primer 习题答案
C++ Primer Answers
Part 1 基本语言
Part 2 容器和算法
Part 3 类和数据抽象
Part 4 面向对象编程与泛型编程
Part 5 高级主题
附录:操作符优先级
Part 1 基本语言
---Chapter 3
#### Exercise 3.1: 用适当的 `using `声明,而不用 `std::`,访问标准库中名字的方法,重新编写第 2.3 节的程序,计算一给定数的给定次幂的结果。 ```C++ #include //读入一行
#include<iostream>
using namespace std;
int main()
{
string word;
while(getline(cin,word))
{
cout<<word<<endl;
}
return 0;
}
//读入单词
#include<iostream>
using namespace std;
int main()
{
string word;
while(cin>>word)
{
cout<<word<<endl;
}
return 0;
}
Exercise 3.8: 编一个程序,从标准输入读取多个 string 对象,把它们连接起来存放到一个更大的 string 对象中。并输出连接后的 string 对象。接着,改写程序,将连接后相邻 string 对象以空格隔开。
#include<iostream>
using namespace std;
int main()
{
string result_str,str;
cin>>result_str;
while(cin>>str)
result_str=result_str+" "+str;
cout<<result_str<<endl;
return 0;
}
Exercise 3.10: 编一个程序,从 string 对象中去掉标点符号。要求输入到程序的字符串必须含有标点符号,输出结果则是去掉标点符号后的 string 对象。
#include<iostream>
using namespace std;
int main()
{
bool has_punct=false;
string s,result_str;
cout<<"Enter a String:"<<endl;
getline(cin,s);
for(string::size_type index=0;index!=s.size();++index)
{
if(ispunct(s[index]))
{
has_punct=true;
}
else
{
result_str+=s[index];
}
}
if(has_punct)
cout<<"Result:"<<result_str<<endl;
else
{
cout<<"No punctuation character in the string"<<endl;
return -1;
}
return 0;
}
Exercise 3.13: 读一组整数到 vector 对象,计算并输出每对相邻元素的和。如果读入元素个数为奇数,则提示用户最后一个元素没有求和,并输出其值。然后修改程序:头尾元素两两配对(第一个和最后一个,第二个和倒数第二个,以此类推),计算每对元素的和,并输出。
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
vector<int> sum;
int ival;
cout << "Enter numbers(Ctrl+Z to end):" << endl;
while(cin>>ival)
ivec.push_back(ival);
if(ivec.size()==0)
{
cout<<"No elements"<<endl;
}
for (vector<int>::size_type index=0;index<ivec.size()-1;index=index+2)
{
if(ivec.size()%2!=0)
{
if(index<ivec.size()-1)
{
sum.push_back(ivec[index]+ivec[index+1]);
//sum.push_back(ivec[index]+ivec[ivec.size()-2-index]);
}
}
else
{
sum.push_back(ivec[index]+ivec[index+1]);
// sum.push_back(ivec[index]+ivec[ivec.size()-1-index]);
}
}
if(ivec.size()%2!=0)
{
cout<<"Last element is not computed:"<<endl;
}
cout << "Sum of each pair of adjacent elements in the vector:"<<endl;
for (vector<int>::size_type index=0;index!=sum.size();++index)
{
cout<<sum[index]<<" ";
}
cout<<endl;
return 0;
}
Exercise 3.14: 读入一段文本到 vector 对象,每个单词存储为 vector 中的一个元素。把 vector 对象中每个单词转化为大写字母。输出 vector 对象中转化后的元素,每八个单词为一行输出。
#include<iostream>
#include<vector>
#include<string>
#include<cctype>
using namespace std;
int main()
{
cout<<"Enter some words:"<<endl;
vector <string> istr;
string iword;
while(cin>>iword)
istr.push_back(iword);
cout<<"After transformation:"<<endl;
for (vector<string>::size_type index=0;index<istr.size();++index)
{
for (string::size_type index2 = 0; index2 != istr[index].size(); ++index2)
{
istr[index][index2]=toupper(istr[index][index2]);
}
cout<<istr[index]<<"\t";
if((index%8==0)&&(index>0))
{
cout<<endl;
}
}
return 0;
}
Exercise 3.17: 重做3.13
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec;
vector<int> sum;
int ival;
cout << "Enter numbers(Ctrl+Z to end):" << endl;
while(cin>>ival)
ivec.push_back(ival);
if(ivec.size()==0)
{
cout<<"No elements"<<endl;
}
for(vector<int>::iterator it=ivec.begin();it!=ivec.end()-1;it=it+2)
{
if(ivec.size()%2!=0)
{
if(it<ivec.end()-2)
{
sum.push_back((*it)+*(it+1));
}
}
else
{
sum.push_back((*it)+*(it+1));
}
}
if(ivec.size()%2!=0)
{
cout<<"Last element is not computed:"<<endl;
}
cout << "Sum of each pair of adjacent elements in the vector:"<<endl;
for(vector<int>::iterator it2=sum.begin();it2!=sum.end();++it2)
{
cout<<*it2<<" ";
}
cout<<endl;
return 0;
}
重做3.14
#include<iostream>
#include<vector>
#include<string>
#include<cctype>
using namespace std;
int main()
{
cout<<"Enter some words:"<<endl;
vector <string> istr;
string iword;
while(cin>>iword)
istr.push_back(iword);
cout<<"After transformation:"<<endl;
for(vector<string>::iterator it=istr.begin();it!=istr.end();++it)
{
for(string::iterator it2=(*it).begin();it2!=(*it).end();++it2)
{
*it2=toupper(*it2);
}
cout<<*it<<"\t";
if((it-istr.begin())%8==0&&it>istr.begin())
{
cout<<endl;
}
}
return 0;
}
Exercise 3.18:编写程序来创建有 10 个元素的 vector 对象。用迭代器把每个元素值改为当前值的 2 倍。
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ivec(10,42);
for (vector<int>::iterator it=ivec.begin();it!=ivec.end();++it)
{
*it=*it*2;
}
for (vector<int>::iterator it=ivec.begin();it!=ivec.end();++it)
{
cout<<*it<<"\t";
}
return 0;
}
Chapter 4
Exercise 4.25: 编写程序比较两个 string 类型的字符串,然后编写另一个程序比较两个C风格字符串的值。
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
const int str_size=80;
char *str1,*str2;
str1=new char[str_size];
str2=new char[str_size];
if(str1==NULL||str2==NULL)
{
cout<<"No enough Memory"<<endl;
return -1;
}
cout<<"Enter two strings:"<<endl;
cin>>str1>>str2;
int result;
result=strcmp(str1,str2);
if(result>0)
cout << "\"" << str1 << "\"" << " is bigger than "<< "\"" << str2 << "\"" << endl;
else if (result < 0)
cout << "\"" << str2 << "\"" << " is bigger than "<< "\"" << str1 << "\"" << endl;
else
cout << "They are equal" << endl;
delete[] str1;
delete[] str2;
return 0;
}
Exercise 4.29:(a) 这两段程序的功能是:执行一个循环次数为1000000 的循环,在该循环的循环体中:创建一个新字符串,将一个已存在的字符串复制给新字符串,然后比较两个字符串,最后释放新字符串。
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
const char *pc = "a very long literal string";
const size_t len = strlen(pc +1); // space to
for (size_t ix = 0; ix != 1000000; ++ix) {
char *pc2 = new char[len + 1]; // allocate the space
strcpy(pc2, pc); // do the copy
if (strcmp(pc2, pc)) // use the new string
; // do nothing
delete [] pc2; // free the memory
}
string str("a very long literal string");
// performance test on string allocation and copy
for (int ix = 0; ix != 1000000; ++ix) {
string str2 = str; // do the copy, automatically allocated
if (str != str2) // use the new string
; // do nothing
}
return 0;
}
Exercise 4.30: 编写程序连接两个C风格字符串字面值,把结果存储在一个C风格字符串中。然后再编写程序连接两个 string 类型字符串,这两个 string 类型字符串与前面的C风格字符串字面值具有相同的内容。
- (Cstring style)
#include <cstring>
int main()
{
const char *cp1 = "Mary and Linda ";
const char *cp2 = "are firends.";
size_t len = strlen(cp1) + strlen(cp2);
char *result_str = new char[len+1];
strcpy(result_str, cp1);
strcat(result_str, cp2);
delete [] result_str;
return 0;
}
- (C++ string style)
#include <string>
using namespace std;
int main()
{
const string str1("Mary and Linda ");
const string str2("are firends.");
string result_str;
result_str = str1;
result_str += str2;
return 0;
}
Exercise 4.31: 编写程序从标准输入设备读入字符串,并把该串存放在字符数组中。描述你的程序如何处理可变长的输入。提供比你分配的数组长度长的字符串数据测试你的程序。
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main()
{
string in_str;// 用于读入字符串的string 对象
const size_t str_size = 10;
char result_str[str_size+1];// 读入字符串
cout << "Enter a string(<=" << str_size<< " characters):" << endl;
cin >> in_str;// 计算需复制的字符的数目
size_t len = strlen(in_str.c_str());
if (len > str_size)
{
len = str_size;
cout << "String is longer than " << str_size<< " characters and is stored only "
<< str_size << " characters!" << endl;
}
// 复制len 个字符至字符数组result_str
strncpy(result_str, in_str.c_str(), len);
// 在末尾加上一个空字符(null 字符)
result_str[len+1] = '\0';
return 0;
}
Exercise 4.32: 编写程序用 int 型数组初始化 vector 对象。
Exercise 4.33: 编写程序把 int 型 vector 复制给 int 型数组.
//int数组赋值给vector<int>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
const size_t arr_size = 8;
int int_arr[arr_size];// 输入数组元素
cout << "Enter " << arr_size << " numbers:" << endl;
for (size_t ix = 0; ix != arr_size; ++ix)
cin >> int_arr[ix];// 用int 型数组初始化vector 对象
vector<int> ivec(int_arr, int_arr + arr_size);
return 0;
}
//vector<int>赋值给int数组
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec;
int ival;// 输入vector 元素
cout << "Enter numbers: (Ctrl+Z to end)" << endl;
while (cin >> ival)
ivec.push_back(ival);// 创建数组
int *parr = new int[ivec.size()];// 复制元素
size_t ix = 0;
for (vector<int>::iterator iter = ivec.begin();iter != ivec.end(); ++iter, ++ix)
parr[ix] = *iter;// 释放数组
delete [] parr;
return 0;
}
Exercise 4.34:编写程序读入一组 string 类型的数据,并将它们存储在 vector 中。接着,把该 vector 对象复制给一个字符指针数组。为 vector 中的每个元素创建一个新的字符数组,并把该 vector 元素的数据复制到相应的字符数组中,最后把指向该数组的指针插入字符指针数组。
Exercise 4.35:输出习题 4.34中建立的 vector 对象和数组的内容。输出数组后,记得释放字符数组。
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
using namespace std;
int main()
{
vector<string> ivec;
string str;
cout<<"Enter strings:"<<endl;
while(cin>>str)
ivec.push_back(str);
char **s2ptr=new char*[ivec.size()];
size_t ix=0;
for (vector<string>::iterator it=ivec.begin();it!=ivec.end();++it,++ix)
{
char *strptr=new char[(*it).size()+1];
strcpy(strptr,(*it).c_str());
s2ptr[ix]=strptr;
// *s2ptr=strptr;
// s2ptr=s2ptr+1;
}
cout << "Content of character arrays:" << endl;
for (ix =0; ix != ivec.size(); ++ix)
cout << *(s2ptr+ix) << endl;
// 释放各个字符数组
for (ix =0; ix != ivec.size(); ++ix)
delete [] s2ptr[ix];
// 释放字符指针数组
delete [] s2ptr;
return 0;
}
Chapter 5
#### Exercise 5.23: 预测下列程序的输出是,并解释你的理由。然后运行该程序,输出的结果和你的预测的一样吗?如果不一样,为什么? int x[10]; int *p = x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(p)/sizeof(*p) << endl;
Answer:在表达式sizeof(x)
中,x 是数组名,该表达式的结果为数组x 所占据的存储空间的字节数,为10 个int
型元素所占据的字节数。表达式sizeof(*x)
的结果是指针常量x 所指向的对象(数组中第一个int 型元素)所占据的存储空间的字节数。
表达式sizeof(p)
的结果是指针变量p 所占据的存储空间的字节数。
表达式sizeof(*p)
的结果是指针变量p 所指向的对象(一个int 型数据)所占据的存储空间的字节数。各种数据类型在不同的系统中所占据的字节数不一定相同因此在不同的系统中运行上述程序段得到的结果不一定相同。在Microsoft Visual C++ .NET 2003系统中,一个int 型数据占据4 个字节,一个指针型数据也占据4 个字节,因此运行上述程序得到的输出结果为:101
Exercise 5.30: 下列语句哪些(如果有的话)是非法的或错误的?
(a) vector<string> svec(10);
(b) vector<string> *pvec1 = new vector<string>(10);
(c) vector<string> **pvec2 = new vector<string>[10];
(d) vector<string> *pv1 = &svec;
(e) vector<string> *pv2 = pvec1;
(f) delete svec;
(g) delete pvec1;
(h) delete [] pvec2;
(i) delete pv1;
(j) delete pv2;
#include<string>
#include<iostream>
#include<vector>
using namespace std;
int main(void)
{
vector<string> svec(10);//
vector<string> *pvec1 = new vector<string>(10);
//创建一个字符串数组(vector<string>),用10初始化,这个数组含有10个string
vector<string> *pvec2 = new vector<string>[10];
//创建十个字符串数组(vector<string>),数组初始大小为默认(0)
vector<string> *pv1 = &svec;
vector<string> *pv2 = pvec1;
cout<<sizeof(svec)<<endl;
cout<<sizeof(*pvec1)<<endl;
cout<<sizeof(*pvec2)<<endl;
cout<<sizeof(pv1)<<endl;
cout<<sizeof(pv2)<<endl;
delete pvec1;//只有一个对象 用delete
delete [] pvec2;//有十个对象 用delete [];
delete pv1;
delete pv2;
}
Chapter 6
Chapter 7
Exercise 7.1:形参和实参有什么区别?
【解答】 形参是在函数定义的形参表中进行定义,是一个变量,其作用域为整个函数。而实参出现在函数调用中,是一个表达式。进行函数调用时,用传递给函数的实参对形参进行初始化。
Exercise 7.13: 编写程序计算数组元素之和。要求编写函数三次,每次以不同的方法处理数组边界。
#include<iostream>
using namespace std;
int sum1(const int *begin,const int *end )
{
int sum=0;
while(begin!=end)
sum+=*begin++;
return sum;
}
int sum2(int a[],size_t size)
{
int sum=0;
for(size_t ix=0;ix!=size;ix++)
{
sum+=a[ix];
}
return sum;
}
int sum3(int *begin,size_t size)
{
int sum=0;
int*p=begin;
while(p!=begin+size)
{
sum+=*p++;
}
return sum;
}
int sum4(const int (&a)[4])
{
int sum=0;
for(size_t ix=0;ix!=4;ix++)
{
sum+=a[ix];
}
return sum;
}
int main()
{
int ia[]={1,2,3,4};
int *p=ia;//等价于int *p=&ia[0]
cout<<sizeof(ia)<<endl;//sizeof()
cout<<(unsigned int)(&ia+1)-(unsigned int)(&ia)<<endl;//
cout<<sizeof(p)<<endl;//sizeof(int *)
//当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。下面是个例子
//不论数组a 的容量是多少,sizeof(a)始终等于sizeof(char *)
cout<<"sum1 is :"<<sum1(ia,ia+4)<<endl;//数组头和数组尾
cout<<"sum2 is :"<<sum2(ia,4)<<endl;//数组形参和数组长度
cout<<"sum3 is :"<<sum3(ia,4)<<endl;//指针形参和数组长度
cout<<"sum3 is :"<<sum4(ia)<<endl;//数组引用形参
return 0;
}
//例子:
#include <iostream>
using namespace std;
void foe(int[]);
int main()
{
int myarray[] = {0,1,2,3,4,5};
cout << sizeof(myarray) / sizeof(*myarray) << endl;
foe(myarray);
return 0;
}
void foe(int a[]){
cout << sizeof(a) / sizeof(*a) << endl;
}
Exercise 7.27: 解释形参、局部变量和静态局部变量的差别。并给出一个有效使用了这三种变量的程序例子。
(1) 形参的作用域为整个函数体,而普通(非静态)局部变量和静态局部变量的作用域 为:从定义处到包含该变量定义的块的结束处。
(2) 形参由调用函数时所传递的实参初始化;而普通(非静态)局部变量和静态局部变量 通常用初始化式进行初始化,且均在程序执行流程第一次经过该对象的定义语句时进行初始化。静态局部变量的初始化在整个程序执行过程中进行一次。
(3) 形参和普通(非静态)局部变量均属自动变量,在每次调用函数时创建,并在函数 结束时撤销;而静态局部变量的生命期却跨越了函数的多次调用,它在创建后直到程序结束时才撤销。
#include <iostream>
using namespace std;
void func();
int n=1; //全局变量
int main()
{
static int a; // 静态局部变量
int b= -10; // 局部变量
cout <<"a:" <<a<<" b:" <<b<<" n:" <<n <<endl;
b+=4;
func();
cout <<"a:" <<a<<" b:" <<b<<" n:" <<n <<endl;
n+=10;
func();
return 0;
}
void func()
{
static int a=2; // 静态局部变量
int b=5; // 局部变量
a+=2;
n+=12;
b+=5;
cout <<"a:" <<a<<" b:" <<b<<" n:" <<n <<endl;
}
Exercise 7.31&7.32&7.33
sales_item.h
#ifndef SALESITEM_H
#define SALESITEM_H
#include <iostream>
#include <string>
class sales_item
{
public:
std::istream& input(std::istream &in);
std::ostream& output(std::ostream &out) const;
double avg_price()const;
bool same_isbn(const sales_item &rhs) const
{
return isbn==rhs.isbn;
}
sales_item():units_sold(0),revenue(0.0){}
private:
std::string isbn;
unsigned int units_sold;
double revenue;
};
#endif
sales_item.cpp
#include "main.h"
#include <iostream>
std::istream& sales_item::input(std::istream& in)
{
double price;
in>>isbn>>units_sold>>price;
if(in)
{
revenue=units_sold*price;
}
else
{
units_sold=0;
revenue=0;
}
return in;
}
std::ostream& sales_item::output(std::ostream& out) const
{
out<<"isbn:"<<isbn<<"\t"<<"units_sold:"<<units_sold<<"\t"
<<"revenue:"<<revenue<<"\t"<<"ave_price:"<<avg_price();
return out;
}
double sales_item::avg_price() const
{
if(units_sold)
return revenue/units_sold;
else
return 0;
}
using namespace std;
int main()
{
sales_item item;
cout<<"the transaction readed is:"<<endl;
while(item.input(cin))
{
item.output(cout);
cout<<endl;
}
return 0;
}
Chapter 8
#### Exercise 8.2: 下面的声明是错误的,指出其错误并改正之:ostream print(ostream os);
答:标准库类型不允许做复制或赋值操作。形参或返回类型不能为流类型,所以上句代码错误,因为它把流类型的对象当做了形参。应改为传递指向该对象的指针或引用:
ostream &print( ostream &os );
Exercise 8.3: 编写一个函数,其唯一的形参和返回值都是 istream& 类型。该个函数应一直读取流直到到达文件结束符为止,还应将读到的内容输出到标准输出中。最后,重设流使其有效,并返回该流。
#include <iostream>
#include <stdexcept>
using namespace std;
istream& f(istream& in)
{
int ival;
while(in>>ival,!in.eof())
{
if(in.bad())
throw runtime_error("IO stream corrupted");
if(in.fail())
{
cerr<<"bad data,try again:";
in.clear();
in.setstate(istream::eofbit);
continue;
}
cout<<ival<<endl;
}
in.clear();
return in;
}
int main()
{
cout<<"Input some numbers(ctrl+z end):\n";
f(cin);
return 0;
}
Exercise 8.6: 由于 ifstream 继承了 istream,因此可将 ifstream 对象传递给形参为 istream 引用的函数。使用第 8.2 节第一个习题编写的函数读取已命名的文件。
#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;
istream& f(istream& in)
{
string ival;
while(in>>ival,!in.eof())
{
if(in.bad())
throw runtime_error("IO stream corrupted");
if(in.fail())
{
cerr<<"bad data,try again:";
in.clear();
in.setstate(istream::eofbit);
continue;
}
cout<<ival<<endl;
}
in.clear();
return in;
}
int main()
{
string filename;
cout<<"Input name of file(ctrl z end):\n";
cin>>filename;
ifstream readname;
readname.open(filename.c_str(),ios::app);
if(!readname)
{
cerr<<"error:cnannot open the input file:"<<filename<<endl;
return -1;
}
f(readname);
return 0;
}
8.3 流状态的查询和控制
( 在保证程序的可靠性时经常遇到的问题 )
C和C++的标准里从来没有定义过
fflush(stdin)
。也许有人会说:“可是我用fflush(stdin)
解决了这个问题,你怎么能说是错的呢?”的确,某些编译器(如VC6)支持用fflush(stdin)
来清空输入缓冲,但是并非所有编译器都要支持这个功能(linux
下的gcc
就不支持),因为标准中根本没有定义fflush(stdin)
。MSDN 文档里也清楚地写着fflush on input stream is an extension to the C standard(fflush
操作输入流是对 C 标准的扩充)。
当然,如果你毫不在乎程序的移植性,用 fflush(stdin)
也没什么大问题。以下是 C99 对 fflush
函数的定义:
/*
* 流状态的查询和控制
* 2010-3-26 wcdj
*/
#include <iostream>
#include <stdexcept>
#include "stdio.h"
using namespace std;
void way1_cleario();
void way2_cleario();
int main()
{
int ival;
cout<<"Enter an interger:";
// read cin and test only for EOF;
// loop is executed even if there are other IO failures
while (cin>>ival, !cin.eof())
{
// input stream is corrupted; bail out
if (cin.bad())
{
throw runtime_error("IO stream corrupted");
}
// bad input
if (cin.fail())
{
cerr<<"bad data, try again/n";// warn the user
// reset the stream
//cin.clear(istream::failbit);// error, why?
cin.clear();// ok
way2_cleario();// eat excess bad stream
//way2_cleario();
continue;// get next input
}
// ok, to process ival
cout<<"ok, to process ival"<<endl;
}
return 0;
}
void way1_cleario()
{
int ival=0;
fflush(stdin); //不良代码,C++ codeblock下运行通过,下面代码输入要带空格
// 在C中,刷新缓冲区可以使用下面这样的代码
// 通过 while 循环把输入流中的余留数据“吃”掉
//一种移植性比较高的清空输入缓冲区办法
// while((ival=getchar())!='/n'&&ival!=EOF);
// clearerr( stdin );
}
void way2_cleario()
{
cin.ignore(200,' ');// 输入的数据必须以空格分开,输入数据一定要有空格
}
Exercise 8.14: 使用 open_file 函数以及第 8.2 节第一个习题编写的程序,打开给定的文件并读取其内容。
#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;
istream& f(istream& in)
{
string ival;
while(in>>ival,!in.eof())
{
if(in.bad())
throw runtime_error("IO stream corrupted");
if(in.fail())
{
cerr<<"bad data,try again:";
in.clear();
in.setstate(istream::eofbit);
continue;
}
cout<<ival<<endl;
}
in.clear();
return in;
}
bool open_file(const string &file)
{
fstream fopen;
fopen.clear();
fopen.open(file.c_str());
if(!fopen)
{
cerr<<"cannot open the file"<<endl;
return -1;
}
string s;
// while(fopen>>s)
// {
// cout<<s<<" ";
// }
f(fopen);
fopen.close();
return 1;
}
int main()
{
cout<<"input name of file is: \n";
string fName;
cin>>fName;
open_file(fName);
return 0;
}
Exercise 8.16: 编写程序将文件中的每一行存储在 vector 容器对象中,然后使用 istringstream 从 vector 里以每次读一个单词的形式读取存储的行
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string line,word;
ifstream input("out.txt");
if(!input)
{
cerr<<"error:can not open input file:"<<endl;
return -1;
}
// 打开后,将文件内容读入string类型的vector容器,每一行存储为
// 该容器对象的一个元素。
vector<string> fileword;
while(getline(input,line))
{
fileword.push_back(line);
input.clear();
}
input.close();
vector<string>::const_iterator it =fileword.begin();
while(it!=fileword.end())
{
istringstream divword(*it);
while (divword>>word)
{
cout<<word<<",";
}
cout<<endl;
++it;
}
return 0;
}
8.4 文件读写
#include <fstream>
#include <iostream>
using namespace std;
const char * filename = "out.txt";
int main()
{
ofstream out;//写入文件处理
out.open("out.txt",ios_base::app);
if (out.is_open())
{
out << "This is a line.";
out << "This is another line.\n";
out.close();
out.clear();
}
char buffer0[256];
ifstream in("out.txt");//读取文件处理
if (! in.is_open())
{
cout << "Error opening file";
return 0;
}
while (!in.eof() )
{
in.getline (buffer0,100);
cout << buffer0 << endl;
}
}
8.5. 字符串流
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
int val1=512,val2=1024;
ostringstream format_message;
format_message<<"val1:"<<val1<<"~"<<"val2:"<<val2<<"\n";
//format_message<<"val1:"<<val1<<"\n"<<"val2:"<<val2<<"\n";//这种输出,得到val1为0
istringstream input_istring(format_message.str());
cout <<format_message.str();
string dump;
input_istring >> dump >> val1 >> dump >> val2;
cout << val1 << " " << val2 << endl; // prints 512 1024
return 0;
}
Part 2 容器和算法
---Chapter 9
#### Exercise 9.12: 编写一个函数,其形参是一对迭代器和一个 int 型数值,实现在迭代器标记的范围内寻找该 int 型数值的功能,并返回一个 bool 结果,以指明是否找到指定数据。 ```c++ #include-
#include
- 1.定义了复制构造函数和析构函数但没有定义赋值操作符。
- 2.定义了复制构造函数和赋值操作符但没有定义析构函数。
- 3.定义了析构函数但没有定义复制构造函数和赋值操作符。
vector<int>::iterator mid = iv.begin() + iv.size()/2;
while (vector<int>::iterator iter != mid)
if (iter == some_val)
iv.insert(iter, 2 * some_val);
(1)当执行it.insert操作之后,迭代器 mid 就失效了,是因为 iv.end() 失效了。
(2)迭代器iter没有初始化;
(3)if语句中错误,因为 if ( *iter == some_val )
可改为:
vector<int>::iterator iter = iv.begin();
while ( iter != iv.begin() + iv.end() / 2 )
{
if ( *iter == some_val )
{
it.insert ( iter, 2 * some_val );
iter += 2; // important
}
else
++iter;
}
Exercise 9.26:
假设有如下 ia 的定义,将 ia 复制到一个 vector 容器和一个 list 容器中。
使用单个迭代器参数版本的 erase 函数将 list 容器中的奇数值元素删除掉,然后将 vector 容器中的偶数值元素删除掉。int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
#include <iostream>
#include <list>
#include <vector>
#include <string>
using namespace std;
int main()
{
int ia[]={0,1,1,2,2,3,5,8,8,13,21,55,89};
vector<int> ivec(ia,ia+13);
list<int> ilist(ia,ia+13);
cout<<"before erase,the elements of ivec are:"<<endl;
for(vector<int>::iterator it=ivec.begin();it!=ivec.end();++it)
{
cout<<*it<<" ";
}
cout<<"\nbefore erase,the elements of ilist are:"<<endl;
for(list<int>::iterator it=ilist.begin();it!=ilist.end();++it)
{
cout<<*it<<" ";
}
for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();)
{
cout<<"\nbefore erase *it:"<<*iter<<endl;
if((*iter)%2==0)
{
iter=ivec.erase(iter);//erase擦除iter迭代器后iter为野指针,要把下一个迭代器值付给它
}
else
{
iter++;//不删除就向后搜索
}
cout<<"after erase *it:"<<*iter<<endl;
}
for(list<int>::iterator iter=ilist.begin();iter!=ilist.end();++iter)
{
cout<<"\n2:before erase *it:"<<*iter<<endl;
if((*iter)%2==1)
{
iter=ilist.erase(iter);//第二种写法,擦除之后将指针往前进一个
--iter;
}
cout<<"2:after erase *it:"<<*iter<<endl;
}
cout << "\n After erase, the elements of ivec are:" << endl;
for ( vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it )
{
cout << *it << " ";
}
cout << "\n After erase, the elements of list are:" << endl;
for ( list<int>::iterator it = ilist.begin(); it != ilist.end(); ++it )
{
cout << *it << " ";
}
return 0;
}
Exercise 9.28: 编写程序将一个 list 容器的所有元素赋值给一个 vector 容器,其中 list 容器中存储的是指向 C 风格字符串的 char* 指针,而 vector 容器的元素则是 string 类型。
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
char *c_arr[] = { "one", "two", "three" };
list<char*> pLst;
pLst.assign( &c_arr[0], &c_arr[0]+3 );
cout << "\tAll the members in the list<char*> pLst are:\n";
for ( list<char*>::iterator it = pLst.begin(); it != pLst.end(); ++it )
{
cout << *it << " ";
}
vector<string> strVec;
strVec.assign( pLst.begin(), pLst.end() );
cout << "\n\tAfter assignment from list, the vector<string> strVec are:\n";
for ( vector<string>::iterator it = strVec.begin(); it != strVec.end(); ++it )
{
cout << *it << " ";
}
cout << endl;
return 0;
}
Exercise 9.38: 已知有如下 string 对象:"ab2c3d7R4E6"
, 编写程序寻找该字符串中所有的数字字符,然后再寻找所有的字母字符。以两种版本编写该程序:第一个版本使用 find_first_of 函数,而第二个版本则使用 find_first_not_of 函数。
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s1("ab2c3d7r4e6");
vector <string::size_type> index;
string s2("0123456789");
string::size_type ix=0;
while((ix=s1.find_first_not_of(s2,ix))!=string::npos)
{
//int id=static_cast<int> (ix);
index.push_back(ix);
++ix;
}
for(vector <string::size_type> ::iterator it=index.begin();it!=index.end();++it)
{
cout<<s1[*it]<<endl;
}
return 0;
}
Exercise 9.39:
已知有如下 string 对象:
string line1 = "We were her pride of 10 she named us:";
string line2 = "Benjamin, Phoenix, the Prodigal"
string line3 = "and perspicacious pacific Suzanne";
string sentence = line1 + ' ' + line2 + ' ' + line3;
编写程序计算 sentence 中有多少个单词,并指出其中最长和最短的单词。如果有多个最长或最短的单词,则将它们全部输出。
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string line1 = "We were her pride of 10 she named us:";
string line2 = "Benjamin, Phoenix, the Prodigal";
string line3 = "and perspicacious pacific Suzanne";
string sentence = line1 + ' ' + line2 + ' ' + line3;
string separators(" :\t,\v\r\n\f");//分隔字符
for(size_t ix=0;ix<separators.size();++ix)
{
cout<<" "<<separators[ix]<<" ";
}
cout<<endl;
string word;
string::size_type maxlen,minlen,wordlen;
vector<string> longestword,shortestword;
string::size_type startpos ,endpos=0;
size_t cnt=0;
cout << "\n The sentence is :\n" << sentence << endl;
while((startpos=sentence.find_first_not_of(separators,endpos))!=string::npos)
{
++cnt;
endpos=sentence.find_first_of(separators,startpos);
if(endpos==string::npos)//如果到达句子的末尾,则认为这个句子只有一个单词
{
wordlen=sentence.size()-startpos;
}
else //否则的话该单词长度为末尾点减初始点阿
wordlen=endpos-startpos;
word.assign(sentence.begin()+startpos,sentence.begin()+startpos+wordlen);
if(cnt==1)
{
maxlen=minlen=wordlen;
longestword.push_back(word);
shortestword.push_back(word);
}
else //处理最长字符和最短字符
{
if(wordlen>maxlen)
{
maxlen=wordlen;
longestword.clear();
longestword.push_back(word);
}
else if(wordlen==maxlen)
{
longestword.push_back(word);
}
if(wordlen<minlen)
{
minlen=wordlen;
shortestword.clear();
shortestword.push_back(word);
}
else if(wordlen==minlen)
{
shortestword.push_back(word);
}
}
}
cout << "\n There are " << cnt << " words in the sentence." << endl;
vector<string>::iterator iter;
// out the longest word
cout << "\n longest word :" << endl;
iter = longestword.begin();
while ( iter != longestword.end() )
{
cout << *iter++ << endl;
}
// out the shortest word
cout << "\n shortest word :" << endl;
iter = shortestword.begin();
while ( iter != shortestword.end() )
{
cout << *iter++ << endl;
}
return 0;
}
Exercise 9.43: 使用 stack 对象处理带圆括号的表达式。遇到左圆括号时,将其标记下来。然后在遇到右加括号时,弹出 stack 对象中这两边括号之间的相关元素(包括左圆括号)。接着在 stack 对象中压入一个值,用以表明这个用一对圆括号括起来的表达式已经被替换。
#include <iostream>
#include <string>
#include <stack>
#include <deque>
using namespace std;
int main( )
{
stack<char> sExp;
string strExp;
cout << " Input a expression: ";
cin >> strExp;
// deal the sExp
string::iterator it = strExp.begin();
while ( it != strExp.end() )
{
if ( *it != ')' )
sExp.push( *it );//如果不是)则将字符push进来,一直push到(为止
else
{
while ( ( sExp.top() != '(' ) && !sExp.empty() )//检测到),现在pop顶元素,一直到(为止
{
sExp.pop();
}
// sExp.pop();
if ( sExp.empty() )//如果检测不到(则没有匹配到(
cout << " It's not matched. " << endl;
else
{
sExp.pop();
sExp.push('@');
}
}
++it;
}
// show out the elements of the stack
cout << "\nThe elements of the stack are:" << endl;
while ( ! sExp.empty() )
{
cout << sExp.top() << endl;
sExp.pop();
}
return 0;
}
Chapter 10
#### Exercise 10.1:编写程序读入一系列 string 和 int 型数据,将每一组存储在一个 pair 对象中,然后将这些 pair 对象存储在 vector 容器里。 ```c++ #includeExercise 10.14:
What is the difference between the map operations count and find?
map 容器的 count 和 find 运算有何区别?
Exercise 10.15:
What kinds of problems would you use count to solve? When might you use find instead?
你认为 count 适合用于解决哪一类问题?而 find 呢?
Exercise 10.16:
Define and initialize a variable to hold the result of a call to find on a map from string to vector of int.
定义并初始化一个变量,用来存储调用键为 string、值为 vector
count 返回被寻找元素的次数,结果为0或者1.find函数返回被寻找元素的迭代器。
count用来查找元素是否存在,find用来查找该键值对应的元素
10.17 The Word Transformation Program 单词转换程序
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
// typedef pair <string,string> Author;
// pair <string,string> author("James","Smith");
// Author proud("Marvell","Joy");
// cout<<author.first<<"\t"<<author.second<<endl;
// cout<<proud.first<<"\t"<<proud.second<<endl;
// pair<string,string> next_auth;
// string first,last;
// while (cin>>first>>last)
// {
// next_auth=make_pair(first,last);
// cout<<next_auth.first<<"\t"<<next_auth.second;
// }
// string str;
// int ival;
// vector< pair<string,int> > pairVec;
// while(cin>>str>>ival)
// {
// pairVec.push_back(make_pair(str,ival));
// }
// cout<<"\nThe content of pairVec is £º \n";
// for(vector< pair<string,int> >::iterator it=pairVec.begin(); it!=pairVec.end(); ++it)
// {
// cout<<it->first<<"\t"<<it->second<<endl;
// }
// map<string,int> WordCount;
// string word;
// while(cin>>word)
// {
// // ++WordCount[word];
// // cout<<"The word "<<word<<" appears for "<<WordCount[word]<<" times "<<endl;
// //WordCount.insert(make_pair(word,1));
// pair<map<string,int>::iterator,bool> ret=WordCount.insert(make_pair(word,1));
// if(!ret.second)
// ++ret.first->second;
//
// }
// map<string,int>::iterator map_it=WordCount.begin();
// // while(map_it!=WordCount.end())
// {
//
//// cout<<map_it->first<<" "<<map_it->second<<endl;
//// ++map_it;
// int occurs = 0;
// if (WordCount.count("foobar")&&WordCount.find("foobar")!=WordCount.end())
// {
// occurs = WordCount["foobar"];
// cout<<"foobar occurs"<<occurs<<" times ";
// }
map<string,string> trans_map;
string key,value;
ifstream map_file("C://Users//allen//Desktop//mapfile.txt");
ifstream input("C://Users//allen//Desktop//input.txt");
if(map_file.is_open()&&input.is_open())
{
while(map_file>>key>>value)
trans_map.insert(make_pair(key,value));
string line;
while(getline(input,line))
{
istringstream stream(line);
string word;
bool firstword=true;
while(stream>>word)
{
map<string,string>::const_iterator map_it=trans_map.find(word);
if(map_it!=trans_map.end())
{
word=map_it->second;
}
if(firstword)
firstword=false;
else
cout<<" ";
cout<<word;
}
cout<<endl;
}
}
else
{
// throw runtime_error("no transformation file");
cout<<"No file information"<<endl;
}
return 1;
}
Exercise 10.23: 编写程序将被排除的单词存储在 vector 对象中,而不是存储在 set 对象中。请指出使用 set 的好处。
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <set>
using namespace std;
void restricted_wc(vector<string> strVec,map<string,int>&wordCount)
{
set<string> excluded;
for ( vector<string>::iterator it = strVec.begin(); it != strVec.end(); ++it )
{
excluded.insert( *it );
}
string word;
cout << " Input some words to count the words in wordCount(map) ( ctrl + z to end): "
<< endl;
cin.clear();
while ( cin >> word )
{
if ( !excluded.count( word ) )
++wordCount[ word ];
}
}
int main()
{
cout << " Input some words to vector<string> excluded ( ctrl + z to end): " << endl;
vector<string> excludedVec;
string excludedWord;
while ( cin >> excludedWord )
excludedVec.push_back( excludedWord );
map<string, int > wordCount;
restricted_wc( excludedVec, wordCount );
cout << "\n\tShow out the map:wordCount: " << endl;
for ( map< string, int >::iterator it = wordCount.begin(); it != wordCount.end(); ++it )
{
cout << " The word '" << (*it).first << "' appears for " << (*it).second << " times. " << endl;
}
return 1;
}
Exercise 10.26:编写程序建立作者及其作品的 multimap 容器。使用 find 函数在 multimap 中查找元素,并调用 erase 将其删除。当所寻找的元素不存在时,确保你的程序依然能正确执行。
#include <iostream>
#include <map>
#include <utility>
#include <string>
using namespace std;
int main()
{
// cout << "Hello world!" << endl;
multimap<string,string> mmapAuthor;
string author,book;
cout<<"input Author and his books:\n";
while(cin>>author>>book)
{
mmapAuthor.insert(make_pair(author,book));
}
string searchitem;
cout<<"which author you wanna search: ";
cin.clear();
cin>>searchitem;
multimap<string,string>::iterator iter=mmapAuthor.find(searchitem);
typedef multimap<string,string>::size_type sz_type;
sz_type amount=mmapAuthor.count(searchitem);//count返回查找元素的数量,find返回第一个指针
for(sz_type cnt=0; cnt!=amount; ++cnt,++iter)
{
cout<<"\n we got the author: "<<iter->first<<endl<<"\t==>>his book:\t"<<iter->second<<endl;
}
amount=mmapAuthor.erase(searchitem);
if(amount)
{
cout<<"\n we have erase"<<amount<<"books of the author"<<endl;
}
cout<<endl;
return 0;
}
multimap< string, string > mmapAuthor;
string author, book;
cin.clear();
cout << " \t=>> Input Author and his Book( ctrl + z to end ):\n";
while ( cin >> author >> book )
{
mmapAuthor.insert( make_pair( author, book ) );
}
typedef multimap<string, string >::iterator author_it;
author_it iter = mmapAuthor.begin();
if ( iter == mmapAuthor.end() )
{
cout << "\n\t Empty multimap! " << endl;
return 0;
}
string currAuthor, preAuthor;
do
{
currAuthor = iter->first;
if ( preAuthor.empty() || currAuthor[0] != preAuthor[0] )
{
cout << "Author Names Beginning with ' "
<< iter->first[0] << " ' : " << endl;
}
cout << currAuthor;
pair< author_it, author_it > pos = mmapAuthor.equal_range( iter->first );
while ( pos.first != pos.second )
{
cout << ", " << pos.first->second;
++pos.first;
}
cout << endl;
iter = pos.second;
preAuthor = currAuthor;
} while ( iter != mmapAuthor.end() );
10.32 test_query 程序
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <set>
using namespace std;
class TextQuery
{
public:
typedef vector<string>::size_type line_no;
void read_file(ifstream &is){store_file(is);build_map();}
set<line_no> run_query(const string&) const;
string text_line(line_no) const;
private:
void store_file(ifstream&);
void build_map();
vector<string> lines_of_text;
map< string,set<line_no> > word_map;
};
void TextQuery::store_file(ifstream& is)
{
string line;
while(getline(is,line))
{
lines_of_text.push_back(line);
}
}
void TextQuery::build_map()
{
for (line_no line_num=0;line_num!=lines_of_text.size();++line_num)
{
istringstream line(lines_of_text[line_num]);
string word;
while(line>>word)
{
word_map[word].insert(line_num);//word_map[word]==set
}
}
}
set<TextQuery::line_no> TextQuery::run_query(const string&query_word) const
{
map< string,set<line_no> >::const_iterator loc=word_map.find(query_word);
if(loc==word_map.end())
return set<line_no>();
else
return loc->second;
}
string TextQuery::text_line(line_no line) const
{
if(line<lines_of_text.size())
return lines_of_text[line];
// throw std::out_of_range("line number out of range");
}
void print_results(const set<TextQuery::line_no>& locs,const string& sought,const TextQuery &file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::size_type size1 =locs.size();
cout<<"\n"<<sought<<" occurs "<<size1<<" "<<" times "<<endl;
line_nums::const_iterator it=locs.begin();
for(;it!=locs.end();++it)
{
cout<<"\t(line "<<(*it)+1<<")"<<file.text_line(*it)<<endl;
}
}
int main()
{
ifstream infile("C://Users//allen//Desktop//input.txt");
TextQuery tq;
tq.read_file(infile);
while(true)
{
cout<<"Enter a word to look for, or q to quit it: ";
string s;
cin>>s;
if(!cin||s=="q")
break;
set<TextQuery::line_no> locs=tq.run_query(s);
print_results(locs,s,tq);
}
return 1;
}
Part 3 类和数据抽象
Chapter 11
Exercise 11.16:重写(第 11.3.2 节第 3 小节)的程序,使用 copy 算法将一个文件的内容写到标准输出中。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;
int main()
{
// istream_iterator <int> cin_it(cin);
// istream_iterator <int> eof;
// vector<int> vec(cin_it,eof);
// sort(vec.begin(),vec.end());
// ostream_iterator <int> output(cout," ");
// unique_copy(vec.begin(),vec.end(),output);
ifstream ifile("C://Users//melo//Desktop//1.txt");
string istr;
vector <string> inputfile;
ostream_iterator <string> output(cout," ");
while(ifile.is_open()&&ifile>>istr)
{
inputfile.push_back(istr);
}
copy(inputfile.begin(),inputfile.end(),output);
return 0;
}
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;
int main()
{
ifstream ifile("C://Users//melo//Desktop//1.txt");
istream_iterator <string> cin_it(ifile);
istream_iterator <string> eof;
ostream_iterator <string> output(cout," ");
vector<string> inputfile(cin_it,eof);
copy(inputfile.begin(),inputfile.end(),output);
//sort(inputfile.begin)
return 0;
}
Exercise 11.18: 编写程序使用 istream_iterator对象从标准输入读入一系列整数。使用 ostream_iterator 对象将其中的奇数写到一个文件中,并在每个写入的值后面加一个空格。同样使用 ostream_iterator 对象将偶数写到第二个文件,每个写入的值都存放在单独的行中。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;
bool IsOdd (int i) { return ((i%2)==1); }
int main()
{
istream_iterator <int> cin_it(cin);
istream_iterator <int> eof;
ofstream outputfile1("C://Users//melo//Desktop//1.txt");
ofstream outputfile2("C://Users//melo//Desktop//2.txt");
if ( !outputfile1 || !outputfile2 )
{
cerr << " File can't be open. " << endl;
return -1;
}
ostream_iterator <int> output1(outputfile1," ");
ostream_iterator <int> output2(outputfile2,"\n");
while(cin_it!=eof)
{
if((*cin_it)%2==0)
{
*output2++=*cin_it++;
}
else
{
*output1++=*cin_it++;
}
//cin_it++;
}
outputfile1.close();
outputfile2.close();
return 0;
}
Exercise 11.21:使用 find 在一个 int 型的 list 中寻找值为 0 的最后一个元素。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
int main()
{
int ia[]={1,2,3,4,0,6};
list <int> iList(ia,ia+6);
list <int> :: reverse_iterator last_0_it=find(iList.rbegin(),iList.rend(),0);
if(last_0_it!=iList.rend())
cout<<"get the last 0,it's value is:"<<*last_0_it<<endl;
else
cout<<endl;
return 0;
}
Chapter 12
#### Exercise 12.13:扩展 Screen 类以包含 move、set 和 display 操作。通过执行如下表达式来测试类:// move cursor to given position, set that character and display the screenmyScreen.move(4,0).set('#').display(cout);
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class Screen
{
public:
typedef std::string::size_type index;
Screen():cursor(0),height(0),width(0),contents("") {};
Screen(index hei,index wid,const string&content=""):cursor(0),height(hei),width(wid),contents(content) {};
Screen& move(index r,index c)
{
index row=r*width;
cursor=row+c;
return *this;
}
Screen& set(char c)
{
contents[cursor]=c;
return *this;
}
Screen& display(ostream& os)
{
do_display(os);
return *this;
}
const Screen& display(ostream &os) const
{
do_display(os);
return *this;
}
private:
string contents;
index cursor;
index height,width;
void do_display(ostream&os)const
{
os<<contents;
}
};
int main()
{
Screen myscreen(5,5,"bbbbbgggggfffffiiiiimmmmm");
myscreen.move(4,0).set('$').display(cout);
return 0;
}
Chapter 13
#### Exercise 13.14:理解复制控制成员和构造函数的一个良好方式是定义一个简单类,该类具有这些成员,每个成员打印自己的名字:
struct Exmpl {
Exmpl() { std::cout << "Exmpl()" << std::endl; }
Exmpl(const Exmpl&)
{ std::cout << "Exmpl(const Exmpl&)" << std::endl; }
// ...
};
编写一个像 Exmpl 这样的类,给出复制控制成员和其他构造函数。然后写一个程序,用不同方式使用 Exmpl 类型的对象:作为非引用形参和引用形参传递,动态分配,放在容器中,等等。研究何时执行哪个构造函数和复制构造函数,可以帮助你融会贯通地理解这些概念。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class exmpl
{
public:
exmpl()
{
cout<<"Using Constructor exmpl"<<endl;
}
exmpl(const exmpl& ex1)
{
cout<<"Using Copy Constructor exmpl(const exmpl&)"<<endl;
}
exmpl& operator=(const exmpl& ex1)
{
cout<<"Using operator="<<endl;
}
~exmpl()
{
cout<<"Using deconstructor"<<endl;
}
};
void func1(exmpl obj)
{
}
void func2(exmpl& obj)
{
}
exmpl func3()
{
exmpl obj;
return obj;
}
int main()
{
exmpl ex1;
exmpl ex2(ex1);
exmpl ex3=ex1;
ex2=ex3;
func1(ex1);
func2(ex1);
ex1=func3();
exmpl *p=new exmpl;
delete p;
vector<exmpl> iVec(3);
return 0;
}
Exercises Section 13.4
A Message-Handling Example:有些类为了做一些工作需要对复制进行控制。为了给出这样的例子,我们将概略定义两个类,
这两个类可用于邮件处理应用程序。Message 类和 Folder 类分别表示电子邮件(或其他)消息和消息所出现的目录,
一个给定消息可以出现在多个目录中。Message 上有 save 和 remove 操作,用于在指定 Folder 中保存或删除该消息。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class Message
{
public:
Message(const string& str=""):contents(str) {}
Message(const Message&);
Message& operator=(const Message&);
~Message()
{
remove_Msg_from_Folders();
}
void save(Folder&);
void remove(Folder&);
void addFldr(Folder*);
void remFldr(Folder*);
private:
string contents;
set<Folder*> folders;
void put_Msg_in_Folders(const set<Folder*> &);
void remove_Msg_from_Folders();
};
class Folder
{
public:
Folder() {}
Folder(const Folder&);
Folder& operator=(const Folder&);
~Folder();
void save(Message&);
void remove(Message&);
void addMsg(Message&);
void remMsg(Message&);
private:
set<Message*> messages;
void put_Fldr_In_Messages(const set<Message*>&);
void remove_Fldr_Messages();
};
/*******************Folder 类 的定义************************/
Folder::Folder(const Folder&f):messages(f.messages)
{
put_Fldr_In_Messages(messages);
}
void Folder::put_Fldr_In_Messages(const set<Message*>&rhs)
{
for(set<Message*>::const_iterator beg=rhs.begin();beg!=rhs.end();++beg)
(*beg)->addFldr(this);
}
Folder& Folder::operator=(const Folder& rhs)
{
if(&rhs!=this)
{
remove_Fldr_Messages();
messages=rhs.messages;
put_Fldr_In_Messages(rhs.messages);
}
return *this;
}
void Folder::remove_Fldr_Messages()
{
for(set<Message*>::const_iterator beg=messages.begin();beg!=messages.end();++beg)
(*beg)->remFldr(this);
}
Folder::~Folder()
{
remove_Fldr_Messages();
}
void Folder::save(Message &msg)
{
addMsg(&msg);
msg.addFldr(this);
}
void Folder::remove(Message& msg)
{
remMsg(&msg);
msg.remFldr(this);
}
void Folder::addMsg(Message & msg)
{
messages.insert(mag);
}
void Folder::remMsg(Message & msg)
{
messages.erase(msg);
}
/*******************Message类 的定义************************/
Message::Message(const Message& m):contents(m.contents),folders(m.folders)
{
put_Msg_in_Folders();
}
Message& Message::operator=(const Message&rhs)
{
if(&rhs!=this)
{
remove_Msg_from_Folders();
contents=rhs.contents;
folders=rhs.folders;
put_Msg_in_Folders(rhs.folders);
}
return *this;
}
void Message::put_Msg_in_Folders(const set<Folder*> &rhs)
{
for(set<Folder*>::const_iterator beg=rhs.begin(); beg!=rhs.end(); ++beg)
(*beg)->addMsg(this);
}
void Message::remove_Msg_from_Folders()
{
for(set<Folder*>::const_iterator beg=folders.begin(); beg!=folders.end(); ++beg)
(*beg)->remMsg(this);
}
void Message::save(Folder &fldr)
{
addFldr(&fldr);
fldr.remMsg(this);
}
void Message::remove(Folder & fldr)
{
remFldr(&fldr);
fldr.remMsg(this);
}
void Message::addFldr(Folder* fldr)
{
folders.insert(fldr);
}
void Message::remFldr(Folder* fldr)
{
folders.erase(fldr);
}
int main()
{
return 0;
}
带指针的类
Exercise 13.24:实现你自己的使用计数的 HasPtr 类的版本
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class HasPtr
{
public:
HasPtr(int *p,int i):ptr(new U_Ptr(p)),val(i){}
HasPtr(const HasPtr&orig):ptr(orig.ptr),val(orig.val){++ptr->use;}
HsaPtr& operator=(const HasPtr& rhs)
{
++rhs.ptr->use;
if(--ptr->use==0)
delete ptr;
ptr=rhs.ptr;
val=rhs.val;
return *this;
}
~HasPtr()
{
if(--ptr->use==0)
delete ptr;
}
int *get_ptr() const {return ptr->ip;}
int get_int() const {return val;}
void set_ptr(int *p){ptr->ip=p;}
void set_int(int i){val=i;}
int get_ptr_val() const {return *(ptr->ip);}
void set_ptr_val(int val) {*(ptr->ip=i);}
private:
U_ptr* ptr;
int val;
};
class U_ptr
{
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int* p) :ip(p),use(1){}
~U_Ptr() {delete p;}
};
int main()
{
return 0;
}
Exercise 13.27:
The valuelike HasPtr class defines each of the copy-control members. Describe what would happen if the class defined
值型 HasPtr 类定义了所有复制控制成员。描述将会发生什么,如果该类:
(a)此种情况下,使用编译器合成的赋值操作符。因此若将一个HasPtr对象赋值给另一个HasPtr对象,则两个对象的ptr成员值相同,
即二者指向同一基础int对象,当其中一个对象被撤销后,该基础int对象也被撤销,使得另一个对象中的ptr成员指向一个失效的基础int对象。
而且当另一个对象被撤销时,会因为析构函数对同一指针进行重复删除而出现内存访问非法的错误。此外,被赋值的HasPtr对象
的ptr成员原来所指向的基础int对象不能再被访问,但也没有被撤销。'
(b) 此时使用编译器合成的析构函数,当一个对象被撤销后,其成员所指向的基础int对象不会被撤销,会一直存在,从而导致内存泄露。
(c) 此时,使用编译器合成的复制构造函数和赋值操作符。当将一个对象赋值给另一个HasPtr对象时或复制构造另一个HasPtr对象时,
则两个HasPtr对象的ptr成员值相同,其他出现的问题与(a)的情况相同。
Part 4 面向对象编程与泛型编程
---Chapter 14
#### Exercises Section 14.2.1Exercise 14.7:
为下面的 CheckoutRecord 类定义一个输出操作符:
class CheckoutRecord {
public:
// ...
private:
double book_id;
string title;
Date date_borrowed;
Date date_due;
pair<string,string> borrower;
vector< pair<string,string>* > wait_list;
};
Exercise 14.8:第 12.4 节的习题中,你编写了下面某个类的框架:为所选择的类编写输出操作符
(a) Book (b) Date (c) Employee
(d) Vehicle (e) Object (f) Tree
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class Date
{
public:
Date(){}
Date(int y,int m,int d):year(y),month(m),day(d){}
friend ostream& operator<<(ostream&,const Date&);
private:
int year,month,day;
};
ostream& operator<<(ostream& out,const Date&d)
{
out<<"year:"<<d.year<<"\t"<<"month:"<<d.month<<"\t"<<"day:"<<d.day<<endl;
}
int main()
{
Date d(1988,12,1);
cout<<d<<endl;
return 0;
}
```c++
#### Exercise 14.12:编写 Sales_item 操作符,用 + 进行实际加法,而 += 调用 +。与本节中操作符的实现方法相比较,讨论这个方法的缺点。
```c++
Sales_item Sales_item::operator+( const Sales_item& rhs )
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
//将下面定义的非成员+=操作符声明为类Sales_item的友元:
Sales_item operator+=( Sales_item& lhs, const Sales_item& rhs )
{
lhs = lhs + rhs;
return lhs;
}
这个方法缺点:在+=操作中需要创建和撤销一个临时Sales_item对象,来保存+操作的结果,没有本节中的方法简单有效。
Exercise 14.15:为第 14.2.1 节习题中介绍的 CheckoutRecord 类定义赋值操作符
Exercise 14.16: CheckoutRecord 类还应该定义其他赋值操作符吗?如果是,解释哪些类型应该用作操作数并解释为什么。为这些类型实现赋值操作符
Exercise 14.17:第 14.2.1 节习题中定义了一个 CheckoutRecord 类,为该类定义一个下标操作符,从等待列表中返回一个名字。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class Date
{
public:
Date(){}//默认构造函数
Date(int y,int m,int d):year(y),month(m),day(d){}//构造函数
Date(Date& d):year(d.year),month(d.month),day(d.day){}//复制构造函数
friend ostream& operator<<(ostream&,const Date&);//输出操作符重载函数
int year,month,day;
};
ostream& operator<<(ostream& out,const Date& d)
{
out<<"year:"<<d.year<<"\t"<<"month:"<<d.month<<"\t"<<"day:"<<d.day<<endl;
}
class CheckoutRecord
{
public:
CheckoutRecord(){}
friend istream& operator>>(istream& ,CheckoutRecord&);
friend ostream& operator<<(ostream& ,CheckoutRecord&);
CheckoutRecord& operator=(const CheckoutRecord&);
private:
double book_id;
string title;
Date date_borrowed;
Date date_due;
pair<string,string> borrower;
vector<pair<string,string>*> wait_list;
};
CheckoutRecord& CheckoutRecord::operator=(const CheckoutRecord& cr)
{
book_id=cr.book_id;
title=cr.title;
date_borrowed=cr.date_borrowed;
date_due=cr.date_borrowed;
borrower.first=cr.borrower.first;
borrower.second=cr.borrower.second;
wait_list.clear();
for(vector <pair<string,string>*>::const_iterator it=cr.wait_list.begin();it!=wait_list.end();++it)
{
pair<string,string> *ppr=new pair<string,string>;
ppr->first=(*it)->first;
ppr->second=(*it)->second;
wait_list.push_back(ppr);
}
return *this;
}
istream& operator>>(istream& in, CheckoutRecord& c)
{
cout << " Input book_id(double) and title(string) :\n";
in >> c.book_id >> c.title;
cout << " Input data_borrowed (3 ints: year, month , day) :\n";
in >> c.date_borrowed.year >>c.date_borrowed.month >>c.date_borrowed.day;
cout << " Input data_due (3 ints: year, month , day) :\n";
in >> c.date_due.year >> c.date_due.month >> c.date_due.day;
cout << " Input the pair<string,string> borrower (string) :\n";
in >> c.borrower.first >> c.borrower.second;
if ( !in )//检查输入是否错误
{
c = CheckoutRecord();
return in;
}
cout << " Input the wait_list (string) :\n";
c.wait_list.clear();
while(in)
{
pair<string,string>* p_pstr=new pair<string,string>;
in>>p_pstr->first>>p_pstr->second;
if(!in)//检查输入是否错误
{
delete p_pstr;
return in;
}
else
{
c.wait_list.push_back(p_pstr);
}
}
return in;
}
ostream& operator<<(ostream& out,CheckoutRecord& s)
{
out << s.book_id << "\t" << s.title<< "\t" << s.date_borrowed<< "\t" << s.date_due<< "\t" ;
out<< " borrower:" <<s.borrower.first << "," << s.borrower.second << endl;
out << " wait_list: " << endl;
for ( vector< pair<string,string>* >::const_iterator it = s.wait_list.begin();it!= s.wait_list.end(); ++it )
{
out << "\t" << (*it)->first << "," << (*it)->second << endl;
}
return out;
}
int main()
{
CheckoutRecord c1;
CheckoutRecord c2;
cin>>c1;
c2=c1;
cout<<c2<<endl;
return 0;
}
有问题:vector<pair<string,string>*>保存的是指针,对象的地址。
Exercises Section 14.6:ScreenPtr类(智能指针)以及操作:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
using namespace std;
class Screen
{
public:
/*****构造函数以及类型声明*****/
typedef string::size_type index;
Screen():cursor(0),height(0),width(0),contents("") {};
Screen(index he,index wd,string content=""):contents(content),height(he),width(wd),cursor(0) {}
/*****操作函数*****/
Screen& move(index r,index c);
Screen& set(char c);
Screen& display(ostream& os);
char get_char() const;
const Screen& display(ostream &os) const;
const char get_char(index r, index c) const;
const index get_cursor() const;
/*****操作符重载函数*****/
Screen& operator=(Screen& s);
private:
string contents;
string::size_type cursor;
string::size_type height,width;
void do_display(ostream&os)const
{
os<<contents<<endl;
}
};
Screen& Screen::move(index r,index c)
{
index row=r*width;
cursor=row+c;
return *this;
}
Screen& Screen::set(char c)
{
contents[cursor]=c;
return *this;
}
Screen& Screen::display(ostream& os)
{
do_display(os);
return *this;
}
char Screen::get_char() const
{
return contents[cursor];
}
Screen& Screen::operator=(Screen& s)
{
contents=s.contents;
height=s.height;
width=s.width;
cursor=s.cursor;
}
const Screen& Screen::display(ostream &os) const
{
do_display(os);
return *this;
}
const char Screen::get_char(index r, index c) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specified character
}
const Screen::index Screen::get_cursor() const
{
return cursor;
}
/*****ScrPtr类*****/
class ScrPtr
{
friend class ScreenPtr;
Screen *sp;
size_t use;
ScrPtr (Screen *p):sp(p),use(1) {}
~ScrPtr()
{
delete sp;
}
};
/*****ScreenPtr类*****/
class ScreenPtr
{
public:
ScreenPtr(Screen *p):ptr(new ScrPtr(p))
{
cout<<"after call the constructor ,use :"<<ptr->use<<endl;
}
ScreenPtr(const ScreenPtr &orig):ptr(orig.ptr)
{
++ptr->use;
cout<<"after call the copy constructor ,use :"<<ptr->use<<endl;
}
ScreenPtr& operator=(const ScreenPtr& sp)
{
++sp.ptr->use;
if ( --ptr->use == 0 )
delete ptr;
ptr = sp.ptr;
return *this;
}
Screen& operator*()
{
return *ptr->sp;
}
Screen* operator->()
{
return ptr->sp;
}
const Screen& operator*( ) const
{
return *ptr->sp;
}
const Screen* operator->( ) const
{
return ptr->sp;
}
~ScreenPtr()
{
if(--ptr->use==0) delete ptr;
cout<<"after call the deconstructor ,use :"<<ptr->use<<endl;
}
private:
ScrPtr *ptr;
};
int main()
{
ScreenPtr s1=new Screen(5,5,"bbbbbgggggfffffiiiiimmmmm");
ScreenPtr s2=s1;
//s1->move()
s1->move(4,0).set('$').display(cout);
(*s2).move(4,0).set('$').display(cout);
return 0;
}
Exercises Section 14.7:
CheckedPtr 类 表示指向数组的指针:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
#include <stdexcept>
using namespace std;
class CheckedPtr
{
public:
CheckedPtr(int*b,int*e):beg(b),end(e),curr(b) {}
CheckedPtr& operator++();
CheckedPtr& operator--();
CheckedPtr operator++(int);
CheckedPtr operator--(int);
int& operator* ();
int& operator[](const size_t index);
const int& operator*() const;
const int& operator[](const size_t index)const;
friend bool operator==(const CheckedPtr& lhs,const CheckedPtr& rhs);
friend bool operator!=(const CheckedPtr& lhs,const CheckedPtr& rhs);
friend bool operator>(const CheckedPtr& lhs,const CheckedPtr& rhs);
friend bool operator<(const CheckedPtr& lhs,const CheckedPtr& rhs);
friend bool operator>=(const CheckedPtr& lhs,const CheckedPtr& rhs);
friend bool operator<=(const CheckedPtr& lhs,const CheckedPtr& rhs);
private:
int* beg;
int* end;
int* curr;
};
CheckedPtr& CheckedPtr::operator++()
{
if(curr==end)
throw out_of_range ("increment past the end of CheckedPtr");
++curr;
return *this;
}
CheckedPtr& CheckedPtr::operator--()
{
if(curr==beg)
throw out_of_range ("decrement past the begining of the CheckedPtr");
--curr;
return *this;
}
CheckedPtr CheckedPtr::operator++(int)
{
CheckedPtr ret(*this);
++(*this);
return ret;
}
CheckedPtr CheckedPtr::operator--(int)
{
CheckedPtr ret(*this);
--(*this);
return ret;
}
int& CheckedPtr::operator*()
{
if(curr==end)
throw out_of_range("invalid ptr");
return *curr;
}
const int& CheckedPtr::operator*() const
{
if(curr==end)
throw out_of_range("invalid ptr");
return *curr;
}
int& CheckedPtr::operator[](const size_t index)
{
if(beg+index>=end||beg+index<beg)
throw out_of_range("invalid index");
return *(beg+index);
}
const int& CheckedPtr::operator[](const size_t index) const
{
if(beg+index>=end||beg+index<beg)
throw out_of_range("invalid index");
return *(beg+index);
}
bool operator==(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return lhs.beg == rhs.beg && lhs.end == rhs.end && lhs.curr == rhs.curr;
}
bool operator!=(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return !( lhs == rhs );
}
bool operator>(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return lhs.beg == rhs.beg && lhs.end == rhs.end
&& lhs.curr > rhs.curr;
}
bool operator<(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return lhs.beg == rhs.beg && lhs.end == rhs.end
&& lhs.curr < rhs.curr;
}
bool operator>=(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return !( lhs.curr < rhs.curr );
}
bool operator<=(const CheckedPtr& lhs,const CheckedPtr& rhs)
{
return !( lhs.curr > rhs.curr );
}
int main()
{
int ia[]={1,2,3,4,5,6};
CheckedPtr c1(ia,ia+6);
cout<<&(*c1)<<endl;//1的地址
cout<<&(*(++c1))<<endl;//2的地址
cout<<&(*(++c1))<<endl;//3的地址
cout<<&(*(++c1))<<endl;//4的地址
//cout<<&(*(c1++))<<endl;//是个临时变量
cout<<*c1++<<endl;
return 0;
}
Exercise 14.33:使用标准库算法和 GT_cls 类,编写一个程序查找序列中第一个比指定值大的元素。
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
#include <stdexcept>
using namespace std;
class GT_cls
{
public:
GT_cls(const string a=""):str(a){}
bool operator()(const string &s)
{
return (s>str);
}
private:
string str;
};
int main()
{
std::cout << " Input some words( ctrl + z to end ):" << std::endl;
vector<string> text;
string word;
while ( std::cin >> word )
{
text.push_back( word );
} // end of input the text
std::cin.clear();
std::cout << " Then , input one given word:\t";
string givenWord;
std::cin >> givenWord;
vector<string>::iterator it = find_if( text.begin(), text.end(), GT_cls( givenWord ));
if ( it != text.end() )
std::cout << "\n Then the first word in the text you input," << std::endl
<< " \twhich is bigger than the given word is:" << std::endl
<< "\t" << *it << std::endl;
return 0;
}
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
#include <stdexcept>
using namespace std;
class Item_base
{
public:
Item_base(const string &book="",
double sales_price=0.0):isbn(book),price(sales_price){}
string book() const{return isbn;}
virtual double net_price(size_t n)const{return n*price;}
virtual void print(ostream &os){os <<price;}
virtual ~Item_base(){}
private:
string isbn;
protected:
double price;
};
class Bulk_item:public Item_base
{
public:
double net_price(size_t cnt)const
{
if(cnt>min_qty)
return cnt*(1-discount)*price;
else
return cnt*price;
}
void print(ostream &os)
{
Item_base::print(ostream &os);
os << " " << discount;
}
private:
size_t min_qty;
double discount;
};
class Ltd_item:public Item_base
{
public:
Led_item(const string& book="",double sales_price,size_t qty=0,double disc_rate=0):item_base(book,sales_price),max_qty(qty),discount(disc_rate){}
double net_price(size_t cnt) const
{
if(cnt<max_qty)
return cnt*(1-discount)*price;
else
return cnt*price-max_qty*discount*price;
}
private:
size_t max_qty;
double discount;
};
int main()
{
return 0;
}
Chapter 15
派生类的构造函数与复制控制Exercise 15.20:回忆在第 13.3 节习题中编写的类,该类的复制控制成员打印一条消息,为 Item_base 和 Bulk_item 类的构造函数增加打印语句。定义复制控制成员,使之完成与合成版本相同的工作外,还打印一条消息。应用使用了 Item_base 类型的那些对象和函数编写一些程序,在每种情况下,预测将会创建和撤销什么对象,并将你的预测与程序所产生的结果进行比较。继续实验,直至你能够正确地预测对于给定的代码片段,会执行哪些复制控制成员
Exercise 15.28:定义一个 vector 保存 Item_base 类型的对象,并将一些 Bulk_item 类型对象复制的到 vector 中。遍历并计算容器中元素的总和。
Exercise 15.29: 重复程序,但这次存储 Item_base 类型对象的指针。比较结果总和。
Exercise 15.30:解释上两题程序所产生总和的差异。如果没有差异,解释为什么没有。
综合:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
#include <stdexcept>
using namespace std;
//基类定义:Item_base
class Item_base
{
public:
Item_base(const string& book="",double sales_price=0):isbn(book),price(sales_price)
{
cout<<"Using Item_base's constructor"<<endl;
}
Item_base(const Item_base& bi)
{
isbn=bi.isbn;
price=bi.price;
cout<<"Using Item_base's copy constructor"<<endl;
}
string book() const
{
return isbn;
}
virtual double net_price(size_t n) const
{
cout<<"Using Item_base's net_price"<<endl;
return price*n;
}
// friend ostream& operator<<(ostream&,const Item_base&);
virtual ~Item_base()
{
cout<<"Using Item_base's destructor"<<endl;
}
private:
string isbn;
protected:
double price;
};
ostream& operator<<(ostream& os,const Item_base &ib)
{
os<<"Using operator<< (ostream&,Item_base&)"<<endl
<<"Item_base's book():"<<ib.book()<<endl
<<"Item_base's net_price():"<<ib.net_price(3)<<endl;
return os;
}
//派生类定义:Bulk_item
class Bulk_item:public Item_base
{
public:
Bulk_item(const string& book,double sales_price,size_t qty=0,double disc=0):
Item_base(book,sales_price),min_qty(qty),discount(disc)
{
cout<<"Using Bulk_item's constructor "<<endl;
}
Bulk_item(const Bulk_item& bi):Item_base(bi)
{
min_qty=bi.min_qty;
discount=bi.discount;
cout<<"Using Bulk_item's copy constructor"<<endl;
}
virtual double net_price(size_t cnt) const
{
cout<<"Using Bulk_item's net_price"<<endl;
if(cnt>min_qty)
return cnt*(1-discount)*price;
else
return cnt*price;
}
~Bulk_item()
{
cout<<"Using Bulk_item's destructor "<<endl;
}
private:
size_t min_qty;
double discount;
};
ostream& operator<<(ostream& os,const Bulk_item& bi)
{
os<<"Using operator<< (ostream&, Bulk_item&)"<<endl
<<"Bulk_item's book():"<<bi.book()<<endl
<<"Bulk_item's net_price():"<<bi.net_price(3)<<endl;
return os;
}
int main()
{
Item_base* p=new Item_base("C++ Primer",42.00);
Bulk_item* p222=new Bulk_item("How to program",42,1,0.2);
cout<<*p<<endl;
cout<<*p222<<endl;
delete p;
delete p222;//析构函数先删除派生类的成员后删除基础类的成员
Item_base p11("C++ Primer",42.00);
Bulk_item p22("How to program",42,1,0.2);
cout<<p11<<endl;
cout<<p22<<endl;
Item_base base0("C++ Primer_0",50);
Bulk_item bulk0("How to program_0",50,1,0.2);
vector<Item_base> basket;
basket.push_back(base0);//此处调用Item_base的复制构造函数
basket.push_back(bulk0);//此处调用Item_base的复制构造函数 生成一个中间变量 传递给basket中元素 再删除这个中间变量
double SumPrice=0;
for (vector<Item_base>::iterator it=basket.begin();it!=basket.end();++it)
{
SumPrice+=(*it).net_price(3);
}
cout<<"\n\tthe sum price of the basket is :\t"<<SumPrice<<endl;
vector<Item_base*> basket2;
Item_base *p1=&base0;
Item_base *p2=&bulk0;
basket2.push_back(p1);
basket2.push_back(p2);
SumPrice = 0.0;
for ( vector<Item_base*>::iterator it = basket2.begin(); it != basket2.end(); ++it )
{
SumPrice += (*it)->net_price(3);
}
cout << "\n\tthe sum price of the basket is :\t" << SumPrice << endl;
return 0;
}
句柄类与继承:item_base(基类)->Bulk_item(派生类)->sales_item(句柄类)->Basket(容器)
Exercise 15.35:编写自己的 compare 函数和 Basket 类的版本并使用它们管理销售
item_base(基类)&Bulk_item(派生类)
//item.h
#ifndef ITEM_H
#define ITEM_H
#include <iostream>
#include <string>
using namespace std;
//基类
class Item_base
{
public:句柄类与继承
Item_base(const string& book="",double sales_price=0):isbn(book),price(sales_price)
{
cout<<"Using Item_base's constructor"<<endl;
}
Item_base(const Item_base& bi)
{
isbn=bi.isbn;
price=bi.price;
cout<<"Using Item_base's copy constructor"<<endl;
}
string book() const
{
return isbn;
}
virtual double net_price(size_t n) const
{
cout<<"Using Item_base's net_price"<<endl;
return price*n;
}
virtual Item_base* clone() const
{
return new Item_base(*this);
}
// friend ostream& operator<<(ostream&,const Item_base&);
virtual ~Item_base()
{
cout<<"Using Item_base's destructor"<<endl;
}
private:
string isbn;
protected:
double price;
};
ostream& operator<<(ostream& os,const Item_base &ib)
{
os<<"Using operator<< (ostream&,Item_base&)"<<endl
<<"Item_base's book():"<<ib.book()<<endl
<<"Item_base's net_price():"<<ib.net_price(3)<<endl;
return os;
}
//打折的派生类
class Bulk_item:public Item_base
{
public:
Bulk_item(const string& book,double sales_price,size_t qty=0,double disc=0):Item_base(book,sales_price),min_qty(qty),discount(disc)
{
cout<<"Using Bulk_item's constructor "<<endl;
}
Bulk_item(const Bulk_item& bi):Item_base(bi)
{
min_qty=bi.min_qty;
discount=bi.discount;
cout<<"Using Bulk_item's copy constructor"<<endl;
}
virtual double net_price(size_t cnt) const
{
cout<<"Using Bulk_item's net_price"<<endl;
if(cnt>min_qty)
return cnt*(1-discount)*price;
else
return cnt*price;
}
virtual Bulk_item* clone() const
{
return new Bulk_item(*this);
}
~Bulk_item()
{
cout<<"Using Bulk_item's destructor "<<endl;
}
private:
size_t min_qty;
double discount;
};
ostream& operator<<(ostream& os,const Bulk_item& bi)
{
os<<"Using operator<< (ostream&, Bulk_item&)"<<endl
<<"Bulk_item's book():"<<bi.book()<<endl
<<"Bulk_item's net_price():"<<bi.net_price(3)<<endl;
return os;
}
#endif
sales_item(句柄类)
//sales_item
#ifndef SALES_ITEM_H
#define SALES_ITEM_H
#include "item.h"
class Sales_item
{
public:
Sales_item():p(0),use(new size_t(1)){}//空的构造函数
Sales_item(const Item_base& item):p(item.clone()),use(new size_t(1)){}//用Item_base类的赋值构造函数
Sales_item(const Sales_item& i):p(i.p),use(i.use){++*use;}//复制构造函数
~Sales_item(){decr_use();}
Sales_item& operator=(const Sales_item&);//复制函数
const Item_base* operator->() const{if(p) return p;else throw logic_error("Unbound Sales_item");}
const Item_base& operator*() const {if(p) return *p;else throw logic_error("Unbound Sales_item");}
private:
Item_base *p;
size_t *use;
void decr_use()
{
if(--*use==0)
{
delete p;
delete use;
}
}
};
Sales_item& Sales_item::operator=(const Sales_item &rhs)
{
++*rhs.use;
decr_use();
p=rhs.p;
use=rhs.use;
return *this;
}
#endif // SALES_ITEM_H
Basket(容器)
//basket.h
#ifndef __BASKET_H_
#define __BASKET_H_
#include "sales_item.h"
#include <set>
inline bool compare(const Sales_item &lhs,const Sales_item& rhs)
{
return lhs->book()<rhs->book();
}
class Basket
{
typedef bool (*Comp)(const Sales_item&, const Sales_item&);
public:
typedef multiset<Sales_item,Comp> set_type;
typedef set_type::size_type size_type;
typedef set_type::const_iterator const_iter;
Basket():items(compare){}
void add_item(const Sales_item& item){items.insert(item);}
size_type size(const Sales_item& i) const {return items.count(i);}
double total() const;
private:
multiset<Sales_item,Comp> items;
};
double Basket::total() const
{
double sum=0;
for(const_iter it=items.begin();it!=items.end();it=items.upper_bound(*it))
{
size_t i=items.count(*it);
cout<<i<<endl;
sum+=(*it)->net_price(i);
}
return sum;
}
#endif
main.cpp
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <fstream>
#include <list>
#include <stdexcept>
#include "basket.h"
#include "sales_item.h"
int main()
{
Basket basket;
Sales_item item1 ( Bulk_item( "0-0001-0001-1", 99, 5, 0.5 ));
//Sales_item item2 ( Bulk_item( "0-0001-0001-2", 50 ));
//Sales_item item3 ( Bulk_item( "0-0001-0001-3", 59, 200, 0.3 ));
//Sales_item item4 ( Bulk_item( "0-0001-0001-1", 99, 20, 0.2 ));
basket.add_item(item1);
//basket.add_item(item2);
//basket.add_item(item3);
//basket.add_item(item4);
cout << basket.total() << endl;
return 0;
}
15.9. Text Queries Revisited
Exercise 15.41:实现 Query 类和 Query_base 类,并为第十章的 TextQuery 类增加需要的 size 操作。通过计算和打印如图 15.4 所示的查询,测试你的应用程序。
TextQuery.h
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <set>
using namespace std;
class TextQuery
{
public:
typedef vector<string>::size_type line_no;
void read_file(ifstream &is)
{
store_file(is);build_map();
}
set<line_no> run_query(const string&) const;
string text_line(line_no) const;
line_no size() const { return lines_of_text.size(); }
private:
void store_file(ifstream&);
void build_map();
vector<string> lines_of_text;
map< string,set<line_no> > word_map;
};
TextQuery.cpp
#include "TextQuery.h"
#include <fstream>
#include <sstream>
using namespace std;
void TextQuery::store_file(ifstream& is)
{
string line;
while(getline(is,line))
{
lines_of_text.push_back(line);
}
}
void TextQuery::build_map()
{
for (line_no line_num=0;line_num!=lines_of_text.size();++line_num)
{
istringstream line(lines_of_text[line_num]);
string word;
while(line>>word)
{
word_map[word].insert(line_num);//word_map[word]==set
}
}
}
set<TextQuery::line_no> TextQuery::run_query(const string&query_word) const
{
map< string,set<line_no> >::const_iterator loc=word_map.find(query_word);
if(loc==word_map.end())
return set<line_no>();
else
return loc->second;
}
string TextQuery::text_line(line_no line) const
{
if(line<lines_of_text.size())
return lines_of_text[line];
}
Query.h
#include "TextQuery.h"
#include <string>
#include <set>
#include <iostream>
#include <fstream>
using namespace std;
/********************************class Query_Base***************************/
//private, abstract class acts as a base for concrete query types
class Query_base
{
public:
friend class Query;
protected:
typedef TextQuery::line_no line_no;
virtual ~Query_base(){}
private:
virtual set<line_no> eval(const TextQuery&) const=0;
virtual ostream& display(ostream& =cout) const=0;
};
/********************************class Query***************************/
class Query
{
//typedef TextQuery::line_no line_no;
public:
Query(const string&);
/****复制控制*****/
Query(const Query& c):q(c.q),use(c.use){++*use;}
~Query(){decr_use();}
Query& operator=(const Query&);
set<TextQuery::line_no> eval(const TextQuery& t) const
{
return q->eval(t);
}
ostream& display(ostream& os) const
{
return q->display(os);
}
friend Query operator~(const Query&);
friend Query operator|(const Query&,const Query&);
friend Query operator&(const Query&,const Query&);
private:
Query(Query_base *query):q(query),use(new size_t(1)){}
Query_base* q;
size_t* use;
void decr_use()
{
if(--*use==0)
{
delete q;
delete use;
}
}
};
inline ostream& operator<<(ostream& os,const Query &q)
{
return q.display(os);
}
inline Query& Query::operator=(const Query& rhs)
{
++*rhs.use;
decr_use();
q=rhs.q;
use=rhs.use;
return *this;
}
/********************************class WordQuery***************************/
class WordQuery:public Query_base
{
public:
friend class Query;
private:
WordQuery(const string& s):query_word(s){}
set<line_no> eval(const TextQuery& t)const
{
return t.run_query(query_word);
}
ostream& display (ostream& os) const
{
return os<<query_word;
}
string query_word;
};
inline Query::Query(const string& word):
q(new WordQuery(word)), use(new std::size_t(1)) {}
/********************************class NotQuery***************************/
class NotQuery:public Query_base
{
friend Query operator~(const Query&);
private:
NotQuery(Query q):query(q){}
set<line_no> eval(const TextQuery&) const;
ostream& display(ostream& os) const
{
return os<<"~("<<query<<")";
}
const Query query;
};
/********************************class BinaryQuery***************************/
class BinaryQuery:public Query_base
{
protected:
BinaryQuery(Query left,Query right,string op):lhs(left),rhs(right),oper(op){}
ostream& display(ostream &os) const
{
return os<<"("<<lhs<<" "<<oper<<" "<<rhs<<")";
}
const Query lhs,rhs;
const string oper;
};
/********************************class AndQuery***************************/
class AndQuery:public BinaryQuery
{
friend Query operator&(const Query&,const Query&);
AndQuery(Query left,Query right):BinaryQuery(left,right,"&"){}
set<line_no> eval(const TextQuery&) const;
};
/********************************class OrQuery***************************/
class OrQuery:public BinaryQuery
{
friend Query operator|(const Query&,const Query&);
OrQuery(Query left,Query right):BinaryQuery(left,right,"|"){}
set<line_no> eval(const TextQuery&) const;
};
inline Query operator&(const Query &lhs,const Query &rhs)
{
return new AndQuery(lhs,rhs);
}
inline Query operator|(const Query &lhs,const Query &rhs)
{
return new OrQuery(lhs,rhs);
}
inline Query operator~(const Query &oper)
{
return new NotQuery(oper);
}
Query.cpp
#include "Query.h"
#include "TextQuery.h"
#include <map>
#include <algorithm>
set<TextQuery::line_no> OrQuery::eval(const TextQuery& file) const
{
set<line_no> right=rhs.eval(file);
set<line_no> ret_lines=lhs.eval(file);
ret_lines.insert(right.begin(),right.end());
return ret_lines;
}
set<TextQuery::line_no> AndQuery::eval(const TextQuery& file) const
{
set<line_no> left=lhs.eval(file);
set<line_no> right=rhs.eval(file);
set<line_no> ret_lines;
set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(ret_lines,ret_lines.begin()));
return ret_lines;
}
set<TextQuery::line_no> NotQuery::eval(const TextQuery& file) const
{
set<line_no> has_val=query.eval(file);
set<line_no> ret_lines;
for(TextQuery::line_no n=0;n!=file.size();++n)
{
if(has_val.find(n)==has_val.end())
ret_lines.insert(n);
}
return ret_lines;
}
main.cpp
#include <iostream>
#include "TextQuery.h"
#include "Query.h"
using namespace std;
void print_result(const set<TextQuery::line_no>& locs, const string& sought, const TextQuery& file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::size_type size = locs.size();
cout<<"\n"<<sought<<" occurs "<<size<<(size>1?" times ":"Time")<<endl;
line_nums::const_iterator it = locs.begin();
for(; it!=locs.end(); ++it)
cout<<(*it)+1<<"\t"<<file.text_line(*it)<<endl;
}
int main()
{
ifstream is("1.txt");
TextQuery tq;
tq.read_file(is);
Query q=Query("fiery")&Query("bird")|Query("wind");
set<TextQuery::line_no> locs=q.eval(tq);
string s("((fiery & bird) | wind)");
print_result(locs,s,tq);
// cout << "Hello world!" << endl;
return 0;
}