复习题
//13.10 //1 基类的公有成员成为派生类的公有成员。基类的保护成员成为派生类的保护成员。 基类的私有成员被继承,但不能直接访问。 //2 不能继承构造函数,析构函数,赋值运算符和友元。 //3 返回类型为void则可以使用单个赋值而不能使用连锁赋值。 如果返回对象而不是引用,则该方法的执行速度变慢,因为返回语句需要复制对象。 //4 按派生的顺序调用构造函数,最早的构造函数最先调用,调用析构函数的顺序正好相反。 //5 需要,每个类都需要自己的构造函数,如果派生类没有添加新成员,构造函数可以为空但必须存在。 //6 只调用派生类方法,它取代基类定义,只有当派生类没有重新定义方法或使用作用域解析运算符时 才会调用基类方法,然而,应该把所有要重新定义的函数声明为虚函数。 //7 派生类构造函数使用new或new[]来初始化类的指针成员,则应该定义一个赋值运算符 更普遍地说,如果对于派生类成员默认赋值不正确,则应该定义赋值运算符。 //8 派生类对象的地址可以赋给基类指针,但只有通过显式类型转换才可以把基类的地址赋给派生类 指针,而使用这样的指针不一定安全。 //9 可以把派生类对象赋给基类对象,派生类中新增的数据成员都不会传递给基类对象,使用基类的 赋值运算符。但是仅当派生类定义了转换运算符(即包含将基类引用作为唯一参数的构造函数) 或使用基类为参数的赋值运算符时,相反方向的赋值才有可能。 //10 C++允许基类引用指向该基类派生来的任何类型。 //11 按值传递对象将调用复制构造函数。由于形参是基类对象,因此调用基类的复制构造函数。复制 构造函数以基类引用为参数,可以接受作为参数传递的派生类对象。最终结果是生成一个新的 基类对象,其成员对应于派生对象的基类部分。 //12 按引用传递可以确保函数从虚函数受益,另外,按引用传递可以节省内存和时间,尤其对于大型对象 按值传递的主要优点在于可以保护原始数据,但可以通过将引用作为const类型传递达到同样的目的。 //13 a,调用基类方法 b,调用派生类方法 //14 首先,这种类型不符合is-a模型,因此公有继承不适用。 其次House的area()定义隐藏了area()的Kitchen版本,不管这两个方法的特征标是否相同。 ps:只要基类定义了virtual,继承类的该函数也具有virtual属性。
practice 1
//classic.h #pragma once #include<string> class Cd { char performers[50]; char label[20]; int selections; double playtime; public: Cd(const char* s1, const char* s2, int n, double x); Cd(); virtual ~Cd(); virtual void Report()const; }; class Classic :public Cd { std::string works; public: Classic(const std::string& s, const char* s1, const char* s2, int n, double x); Classic(); virtual ~Classic(); virtual void Report()const; Classic& operator=(const Classic& cl); }; //classic.cpp #include<iostream> #include"classic.h" using std::cout; using std::endl; Cd::Cd(const char* s1, const char* s2, int n, double x) : selections(n), playtime(x) { strcpy_s(performers, s1); strcpy_s(label, s2); } Cd::Cd() :performers("null"), label("null"), selections(0), playtime(0) {} Cd::~Cd() {} void Cd::Report()const { cout << "performers: " << performers << endl; cout << "label: " << label << endl; cout << "selections: " << selections << endl; cout << "playtime: " << playtime << endl; } Classic::Classic(const std::string& s, const char* s1, const char* s2, int n, double x) : works(s), Cd(s1, s2, n, x) {} Classic::Classic() : works("null"), Cd() {} Classic::~Classic() {} void Classic::Report()const { cout << "works: " << works << endl; Cd::Report(); } Classic& Classic::operator=(const Classic& cl) { if (this == &cl) return *this; Cd::operator=(cl); works = cl.works; return *this; } //main.cpp #include<iostream> #include"classic.h" using namespace std; void Bravo(const Cd& disk) { disk.Report(); } int main() { Cd c1("Beatles", "Capitol", 14, 35.5); Classic c2 = Classic("pianosonata in B flat, Fantasia in C", "Alfred Brendel", "Philips", 2, 57.17); Cd* pcd = &c1; cout << "Using object directly:\n"; c1.Report(); cout << endl; c2.Report(); cout << endl << endl; cout << "Using type cd* pointer to objects:\n"; pcd->Report(); pcd = &c2; cout << endl; pcd->Report(); cout << endl << endl; cout << "Calling a function with a Cd reference argument:\n"; Bravo(c1); cout << endl; Bravo(c2); cout << endl << endl; cout << "Testing assignment:\n"; Classic copy; copy = c2; copy.Report(); return 0; }
practcie 2
只改变实现不改变接口,因此main.cpp文件不用更改
//classic.h #pragma once class Cd { char* performers; char* label; int selections; double playtime; public: Cd(const char* s1, const char* s2, int n, double x); Cd(const Cd& c); Cd(); virtual ~Cd(); virtual void Report()const; Cd& operator=(const Cd& c); }; class Classic :public Cd { char* works; public: Classic(const char* s, const char* s1, const char* s2, int n, double x); Classic(const Classic& cl); Classic(); virtual ~Classic(); virtual void Report()const; Classic& operator=(const Classic& cl); }; //classic.cpp #include<iostream> #include"classic.h" #include<string> #pragma warning(disable :4996) using std::cout; using std::endl; using std::strcpy; using std::strlen; Cd::Cd(const char* s1, const char* s2, int n, double x) : selections(n), playtime(x) { performers = new char[strlen(s1) + 1]; strcpy(performers, s1); label = new char[strlen(s2) + 1]; strcpy(label, s2); } Cd::Cd(const Cd& c) { performers = new char[strlen(c.performers) + 1]; strcpy(performers, c.performers); label = new char[strlen(c.label) + 1]; strcpy(label, c.label); selections = c.selections; playtime = c.playtime; } Cd::Cd() :selections(0), playtime(0) { performers = nullptr; label = nullptr; } Cd::~Cd() { delete[]performers; delete[]label; } void Cd::Report()const { cout << "performers: " << performers << endl; cout << "label: " << label << endl; cout << "selections: " << selections << endl; cout << "playtime: " << playtime << endl; } Cd& Cd::operator=(const Cd& c) { delete[]performers; delete[]label; performers = new char[strlen(c.performers) + 1]; label = new char[strlen(c.label) + 1]; strcpy(performers, c.performers); strcpy(label, c.label); selections = c.selections; playtime = c.playtime; return *this; } Classic::Classic(const char* s, const char* s1, const char* s2, int n, double x) : Cd(s1, s2, n, x) { works = new char[strlen(s) + 1]; strcpy(works, s); } Classic::Classic(const Classic& cl) : Cd(cl) { works = new char[strlen(cl.works) + 1]; strcpy(works,cl.works); } Classic::Classic() : works(nullptr), Cd() {} Classic::~Classic() { delete[]works; } void Classic::Report()const { cout << "works: " << works << endl; Cd::Report(); } Classic& Classic::operator=(const Classic& cl) { if (this == &cl) return *this; Cd::operator=(cl); delete[]works; works = new char[strlen(cl.works) + 1]; strcpy(works, cl.works); return *this; }
practice 3
这题有一个巨坑,书上的13.10有一个continue用来清除输入,但是书上的程序是读取的数字,用的cin,而练习题要用cin.getline(),而且由于前面用的cin读取1,2,3判断创建哪一种类,需要一个cin.get()读取换行符。这时候巨坑出现了,如果按书上的程序抄的同时发现没有用cin.get()导致color和style读不进去然后加上cin.get(),我换了七八个位置加cin.get(),能解决问题,但是会要多读一个换行符才能进去下一个循环,我这个憨批花了两个小时一直没发现问题,以为是cin.get()的问题,现在想想就是因为有个while——continue那个循环,必须读一个字符,所以要多输入一个换行符,现在人已经被自己气死了。。
//dma.h #pragma once #include<iostream> class base { char* label; int rating; public: base(const char* l = "null", int r = 0); base(const base& b); virtual ~base() = 0; base& operator=(const base& b); virtual void View() = 0; }; ///////////////////////////////////////////////////// class baseDMA :public base { public: baseDMA(const char* l = "null", int r = 0); virtual ~baseDMA(); virtual void View(); }; ////////////////////////////////////////////////////////// class lacksDMA :public base { enum { COL_LEN = 40 }; char color[COL_LEN]; public: lacksDMA(const char* c = "blank", const char* l = "null", int r = 0); lacksDMA(const char* c, const base& b); virtual ~lacksDMA(); virtual void View(); }; ////////////////////////////////////////////////////////// class hasDMA :public base { char* style; public: hasDMA(const char* s = "none", const char* l = "null", int r = 0); hasDMA(const char* s, const base& b); hasDMA(const hasDMA& hd); virtual ~hasDMA(); hasDMA& operator=(hasDMA& hd); virtual void View(); }; //dma.cpp #include"dma.h" #pragma warning(disable :4996) using std::cout; using std::endl; base::base(const char* l, int r) :rating(r) { label = new char[std::strlen(l) + 1]; strcpy(label, l); } base::base(const base& b) :rating(b.rating) { label = new char[std::strlen(b.label) + 1]; strcpy(label, b.label); } base::~base() { delete[]label; } base& base::operator=(const base& b) { if (this == &b) return *this; delete[]label; label = new char[std::strlen(b.label) + 1]; strcpy(label, b.label); rating = b.rating; return *this; } void base::View() { cout << "label: " << label << endl; cout << "rating: " << rating << endl; } ///////////////////////////////////////////////////////////// baseDMA::baseDMA(const char* l, int r) :base(l, r) {} baseDMA::~baseDMA() {} void baseDMA::View() { base::View(); } ///////////////////////////////////////////////////////////// lacksDMA::lacksDMA(const char* c, const char* l, int r) : base(l, r) { strcpy_s(color, c); } lacksDMA::lacksDMA(const char* c, const base& b) : base(b) { std::strcpy(color, c); } lacksDMA::~lacksDMA() {} void lacksDMA::View() { base::View(); cout << "color: " << color << endl; } ///////////////////////////////////////////////////////////////// hasDMA::hasDMA(const char* s, const char* l, int r) :base(l, r) { style = new char[std::strlen(s) + 1]; strcpy_s(style, std::strlen(s) + 1, s); } hasDMA::hasDMA(const char* s, const base& b) : base(b) { style = new char[std::strlen(s) + 1]; strcpy_s(style, std::strlen(s) + 1, s); } hasDMA::hasDMA(const hasDMA& hd) : base(hd) { style=new char[std::strlen(hd.style) + 1]; strcpy_s(style, std::strlen(hd.style) + 1, hd.style); } hasDMA::~hasDMA() { delete[]style; } hasDMA& hasDMA::operator=(hasDMA& hd) { if (this == &hd) return *this; base::operator=(hd); delete[]style; style = new char[std::strlen(hd.style) + 1]; strcpy_s(style, std::strlen(hd.style) + 1, hd.style); return *this; } void hasDMA::View() { base::View(); cout << "style: " << style << endl; } //main.cpp #include<iostream> #include"dma.h" using namespace std; int main() { base* pbase[4]; char label[40]; int rating = 0; char color[40]; char style[40]; char kind; for (int i = 0; i < 4; i++) { cout << "Enter label:"; cin.getline(label, 40); cout << "Enter rating:"; cin >> rating; cout << "Enter 1 for baseDMA, 2 for lacksDMA, 3 for hasDMA:"; while (cin >> kind && (kind != '1' && kind != '2' && kind != '3')) cout << "Enter either 1,2 or 3:"; cin.get(); if (kind == '1') pbase[i] = new baseDMA(label, rating); else if (kind == '2') { cout << "Enter color:"; cin.getline(color, 40); pbase[i] = new lacksDMA(color, label, rating); } else { cout << "Enter style:"; cin.getline(style, 40); pbase[i] = new hasDMA(style, label, rating); } } cout << endl; for (int i = 0; i < 4; i++) { pbase[i]->View(); cout << endl; } for (int i = 0; i < 4; i++) delete pbase[i]; cout << "Done!\n"; return 0; }
practice 4
c.赋值运算符和友元函数是无法继承的,也不需要重新定义,而是每个类各自编写, 每个类都只会使用自己的operator=()(赋值运算符)和operator<<()(友元函 数),因此也就不需要声明为虚函数 b.首先构造函数和Show()因为有了新成员需要重新定义,析构函数,复制构造函数, 赋值运算符因为使用了char*指针,需要用new[]分配内存,所以三者都要重新定义, +=和-=运算符重载,它们对两个类的行为都是一样的,不需要重新定义
//port.h #pragma once #include<iostream> using namespace std; class Port { char* brand; char style[20]; int bottles; public: Port(const char* br = "none", const char* st = "none", int b = 0); Port(const Port& p); virtual~Port() { delete[]brand; } Port& operator=(const Port& p); Port& operator+=(int b); Port& operator-=(int b); int BattleCount() { return bottles; } virtual void Show()const; friend ostream& operator<<(ostream& os, const Port& p); }; class VintagePort :public Port { char* nickname; int year; public: VintagePort(); VintagePort(const char* br, const char* st, int b, const char* nn, int y); VintagePort(const VintagePort& vp); ~VintagePort() { delete[]nickname; } VintagePort& operator=(const VintagePort& vp); void Show()const; friend ostream& operator<<(ostream& os, const VintagePort& vp); }; //port.cpp #include"port.h" Port::Port(const char* br = "none", const char* st = "none", int b = 0) : bottles(b) { brand = new char[strlen(br) + 1]; strcpy_s(brand, strlen(br) + 1, br); strcpy_s(style, st); } Port::Port(const Port& p) { brand = new char[strlen(p.brand) + 1]; strcpy_s(brand, strlen(p.brand) + 1, p.brand); strcpy_s(style, p.style); bottles = p.bottles; } Port& Port::operator=(const Port& p) { if (this == &p) return *this; delete[]brand; strcpy_s(brand, strlen(p.brand) + 1, p.brand); strcpy_s(style, p.style); bottles = p.bottles; } Port& Port::operator+=(int b) { bottles += b; return *this; } Port& Port::operator-=(int b) { bottles -= b; return *this; } void Port::Show()const { cout << "Brand: " << brand << endl; cout << "Style: " << style << endl; cout << "Bottles: " << bottles << endl; } ostream& operator<<(ostream& os, const Port& p) { os << p.brand << " " << p.style << " " << p.bottles << " "; } //////////////////////////////////////////////////////////// VintagePort::VintagePort() :Port() { nickname = nullptr; year = 0; } VintagePort::VintagePort(const char* br, const char* st, int b, const char* nn, int y) : Port(br, st, b), year(y) { nickname = new char[strlen(nn) + 1]; strcpy_s(nickname, strlen(nn) + 1, nn); } VintagePort::VintagePort(const VintagePort& vp) : Port(vp), year(vp.year) { nickname = new char[strlen(vp.nickname) + 1]; strcpy_s(nickname, strlen(vp.nickname) + 1, vp.nickname); } VintagePort& VintagePort::operator=(const VintagePort& vp) { if (this == &vp) return *this; Port::operator=(vp); delete[]nickname; nickname = new char[strlen(vp.nickname) + 1]; strcpy_s(nickname, strlen(vp.nickname) + 1, vp.nickname); year = vp.year; return *this; } void VintagePort::Show()const { Port::Show(); cout << "Nickname: " << nickname << endl; cout << "Year: " << year << endl; } ostream& operator<<(ostream& os, const VintagePort& vp) { os << (const Port&)vp; os << vp.nickname << " " << vp.year << " "; }