From 93d6c119b51fdd5e332556798c0c42978b0923c9 Mon Sep 17 00:00:00 2001 From: fyang Date: Sat, 4 Jun 2022 14:55:06 +0800 Subject: [PATCH 1/5] Add review note for chapter -- 13 --- 13-class_inheritance/exercises/exercises.md | 1 - 13-class_inheritance/review/review.md | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/13-class_inheritance/exercises/exercises.md b/13-class_inheritance/exercises/exercises.md index 9daeafb..e69de29 100644 --- a/13-class_inheritance/exercises/exercises.md +++ b/13-class_inheritance/exercises/exercises.md @@ -1 +0,0 @@ -test diff --git a/13-class_inheritance/review/review.md b/13-class_inheritance/review/review.md index b17b8e2..5063899 100644 --- a/13-class_inheritance/review/review.md +++ b/13-class_inheritance/review/review.md @@ -58,6 +58,27 @@ virtual Type_Name Fun_Name(Para List...) = 0; ![vtbl](vtbl.svg "vtbl") +调用函数时,程序将查看存储在对象中的 `vtbl` 地址,然后转向相应的函数地址表。如果使用类声明中定义的第一个虚函数,则程序将使用数组中的第一个函数地址,并执行具有该地址的函数。如果使用类声明中的第三个虚函数,程序将使用地址为数组中第三个元素的函数。 + +总之,使用虚函数时,在内存和执行速度方面有一定的成本,包括: +- 每个对象都将增大,增大量为存储地址的空间; +- 对于每个类,编译器都创建一个虚函数地址表(数组); +- 对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址。 + +虽然非虚函数的效率比虚函数稍高,但不具备动态联编功能。 + +- **有关虚函数注意事项** + - 在基类方法的声明中使用关键字 `vritual` 可以使该方法在基类以及所有的派生类(包括从派生类派生出来的类)中是虚的。 + - 如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法,而不使用为引用或指针类型定义的方法。这称为动态联编或晚期联编。这种行为非常重要,因为这样基类指针或引用可以指向派生类对象。 + - 如果定义的类将被用作基类,则应将那些要在派生类中重新定义额类方法声明为虚的。 + +1. 构造函数 + +构造函数不能是虚函数。创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数,这种顺序不同于继承机制。因此,派生类不继承基类的构造函数,所以将类构造函数声明为虚的没什么意义。 + +2. 析构函数 + + #### 3. 静态联编和动态联编 程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定函数代码块被称为函数名联编(binding) 。在 C 语言中,这非常简单,因为每个函数名都对应一个不同的函数。 由于 C++ 中为函数重载的功能编译器会在编译过程中依据代码选择匹配的函 -- Gitee From 3af9293286c54f3dd5b0c14948ba4f801c5b4fd1 Mon Sep 17 00:00:00 2001 From: fyang Date: Sat, 4 Jun 2022 17:23:21 +0800 Subject: [PATCH 2/5] Add review note for chapter -- 13 --- 13-class_inheritance/review/review.md | 94 +++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/13-class_inheritance/review/review.md b/13-class_inheritance/review/review.md index 5063899..fa37a02 100644 --- a/13-class_inheritance/review/review.md +++ b/13-class_inheritance/review/review.md @@ -78,7 +78,101 @@ virtual Type_Name Fun_Name(Para List...) = 0; 2. 析构函数 +析构函数应当是虚函数,除非类不用做基类。例如,假设 `Employee` 是基类, `Singer` 是派生类,并添加一个 `char *` 成员,该成员指向由 `new` 分配的内存。当 `Signer` 对象过期时,必须调用 `~Singer()` 析构函数来释放内存。 +```cpp +Employee *pe = new Singer; // legal because Employee is base for Singer +// ... +delete pe; +``` +如果使用默认的静态联编,`delete` 语句将调用 `~Employee()` 析构函数。这将释放由 `Singer` 对象中的 `Employee` 部分指向的内存,但不会释放新的类成员指向的内存。但如果析构函数是虚的,则上述代码将先调用 `~Singer` 析构函数释放由 `Singer` 组件指向的内存,然后,调用 `Employee()` 析构函数来释放由 `Employee` 组件指向的内存。 + +这意味着,即使基类不需要显示析构函数提供服务,也不应依赖于默认构造函数,而应提供虚析构函数,即使他不执行任何操作; +```cpp +virtual ~BaseClass() {} +``` +给类定义一个虚析构函数并非错误,即使这个类不用做基类;这只是一个效率方面的问题。 + +*Tips:* 通常应给基类提供一个虚析构函数,即使它并不需要析构函数。 + +3. 友元 +友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数。如果由于这个原因引起了设计问题,可以通过让友元函数使用虚函数来解决。 + +4. 没有重新定义 +如果派生类没有重新定义函数,将使用该函数的基类版本。如果派生类位于派生链中,则将使用最新的虚函数版本,例外的情况是基类版本是隐藏的。 + +5. 重新定义将隐藏方法 +```cpp +class Dwelling +{ +public: + virtual void showperks(int a) const; + // ... +}; +class Hovel : public Dwelling +{ +public: + virtual void showperks() const; + // ... +}; +``` +这将导致问题,可能会出现类似于下面这样的编译器警告: +```cpp +Warning: Hovel::showperks(void) hides Dwelling::showperks(int) +``` +也可能不会出现警告。但不管结果怎样,代码将具有如下含义: +```cpp +Hovel trump; +trump.showperks(); // valid +trump.showperks(5); // invalid +``` +新定义将 `showperks()` 定义为一个不接受任何参数的函数。重定义不会生成函数的两个重载版本,而是隐藏了接受一个 `int` 参数的基类版本。总之,重定义继承的方法并不是重载。如果重定义派生类中的函数,将不只是使用相同的函数参数列表覆盖基类声明,无论参数列表是否相同,该操作将隐藏所有的同名基类方法。 + +这引出了两条经验规则:第一,如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的)。这种特性被称为返回类型协变(covariance of return type),因为允许返回类型随类类型的变化而变化: +```cpp +class Dwelling +{ +public: + // a base method + virtual Dwelling &build(int n); + // ... +}; +class Hovel : public Dwelling +{ +public: + // a derived method with a covariant return type + virtual Hovel & build(int n); // same function signature + // ... +}; +``` +注意,这种例外只适用于返回值,而不适用于参数。 +第二,如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。 +```cpp +class Dwelling +{ +public: + // three overloaded showperks() + virtual void showperks(int a) const; + virtusl void showperks(double x) const; + virtual void showperks() const; + // ... +}; +class Hovel : public Dwelling +{ +public: + // three redefined showperks() + virtual void showperks(); + virtual void showperks(int a) const; + virtual void showperks(double x) const; + virtual void showperks() const; + // ... +}; +``` +如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象将无法使用它们。注意,如果不需要修改,则新定义可只调用基类版本: +```cpp +void Hovel::showperks() const {Dwelling::showperks();} +``` + #### 3. 静态联编和动态联编 程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定函数代码块被称为函数名联编(binding) 。在 C 语言中,这非常简单,因为每个函数名都对应一个不同的函数。 由于 C++ 中为函数重载的功能编译器会在编译过程中依据代码选择匹配的函 -- Gitee From c41ca52656988a06e5e5e89ab2e29f35e6911eca Mon Sep 17 00:00:00 2001 From: fyang Date: Sun, 5 Jun 2022 23:12:59 +0800 Subject: [PATCH 3/5] Add exercises code for chapter -- 13 --- 13-class_inheritance/acctabc/acctabc.h | 2 +- 13-class_inheritance/exercises/p3/dma.cpp | 123 +++++++++++++++++++ 13-class_inheritance/exercises/p3/dma.h | 60 +++++++++ 13-class_inheritance/exercises/p3/usedma.cpp | 34 +++++ 13-class_inheritance/exercises/p4/bop.cpp | 110 +++++++++++++++++ 13-class_inheritance/exercises/p4/bop.h | 40 ++++++ 13-class_inheritance/exercises/p4/main.cpp | 29 +++++ 7 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 13-class_inheritance/exercises/p3/dma.cpp create mode 100644 13-class_inheritance/exercises/p3/dma.h create mode 100644 13-class_inheritance/exercises/p3/usedma.cpp create mode 100644 13-class_inheritance/exercises/p4/bop.cpp create mode 100644 13-class_inheritance/exercises/p4/bop.h create mode 100644 13-class_inheritance/exercises/p4/main.cpp diff --git a/13-class_inheritance/acctabc/acctabc.h b/13-class_inheritance/acctabc/acctabc.h index e3e2513..2e56ecf 100644 --- a/13-class_inheritance/acctabc/acctabc.h +++ b/13-class_inheritance/acctabc/acctabc.h @@ -60,4 +60,4 @@ public: void ResetOwes() { owesBank = 0, 0; } }; -#endif \ No newline at end of file +#endif diff --git a/13-class_inheritance/exercises/p3/dma.cpp b/13-class_inheritance/exercises/p3/dma.cpp new file mode 100644 index 0000000..ea3d5c2 --- /dev/null +++ b/13-class_inheritance/exercises/p3/dma.cpp @@ -0,0 +1,123 @@ +#include "dma.h" +#include + +ABC::ABC(const char *l, int r) +{ + label = new char[strlen(l) + 1]; + strcpy(label, l); + rating = r; +} + +ABC::ABC(const ABC &rs) +{ + label = new char[strlen(rs.label) + 1]; + strcpy(label, rs.label); + rating = rs.rating; +} + +ABC::~ABC() +{ + delete[] label; +} + +ABC &ABC::operator=(const ABC &rs) +{ + if (this == &rs) + return *this; + delete[] label; + label = new char[strlen(rs.label) + 1]; + strcpy(label, rs.label); + rating = rs.rating; + return *this; +} + +ostream &operator<<(ostream &os, const ABC &rs) +{ + os << "Label: " << rs.label << endl; + os << "Rating: " << rs.rating << endl; + + return os; +} + +void ABC::View() const +{ + cout << "Label: " << label << endl; + cout << "Rating: " << rating << endl; +} + +void baseDMA::View() const +{ + ABC::View(); +} + +lacksDMA::lacksDMA(const char *l, int r, const char *c) : ABC(l, r) +{ + strncpy(color, c, COL_LEN - 1); + color[COL_LEN - 1] = '\0'; +} + +lacksDMA::lacksDMA(const ABC &rs, const char *c) : ABC(rs) +{ + strncpy(color, c, COL_LEN - 1); + color[COL_LEN - 1] = '\0'; +} + +ostream &operator<<(ostream &os, const lacksDMA &ls) +{ + os << (const ABC &)ls; + os << "Color: " << ls.color << endl; + return os; +} + +void lacksDMA::View() const +{ + ABC::View(); + cout << "Color: " << color << endl; +} + +hasDMA::hasDMA(const char *l, int r, const char *s) : ABC(l, r) +{ + style = new char[strlen(s) + 1]; + strcpy(style, s); +} + +hasDMA::hasDMA(const ABC &rs, const char *s) : ABC(rs) +{ + style = new char[strlen(s) + 1]; + strcpy(style, s); +} + +hasDMA::hasDMA(const hasDMA &hs) : ABC(hs) +{ + cout << "Copy construct function." << endl; + style = new char[strlen(hs.style) + 1]; + strcpy(style, hs.style); +} + +hasDMA::~hasDMA() +{ + delete[] style; +} + +hasDMA &hasDMA::operator=(const hasDMA &hs) +{ + if (this == &hs) + return *this; + ABC::operator=(hs); // function notation + delete[] style; + style = new char[strlen(hs.style) + 1]; + return *this; +} + +ostream &operator<<(ostream &os, const hasDMA &hs) +{ + os << (const ABC &)hs; + os << "Style: " << hs.style << endl; + return os; +} + +void hasDMA::View() const +{ + ABC::View(); + cout << "Style: " << style << endl; +} \ No newline at end of file diff --git a/13-class_inheritance/exercises/p3/dma.h b/13-class_inheritance/exercises/p3/dma.h new file mode 100644 index 0000000..2b81276 --- /dev/null +++ b/13-class_inheritance/exercises/p3/dma.h @@ -0,0 +1,60 @@ +#ifndef __DMA_H__ +#define __DMA_H__ + +#include +using namespace std; + +class ABC +{ +private: + char *label; + int rating; + +public: + ABC(const char *l = "null", int r = 0); + ABC(const ABC &rs); + virtual ~ABC(); + ABC &operator=(const ABC &s); + friend ostream &operator<<(ostream &os, const ABC &rs); + virtual void View() const; +}; + +class baseDMA : public ABC +{ +public: + baseDMA(const char *l = "null", int r = 0) : ABC(l, r) {} + baseDMA(const baseDMA &rs) : ABC(rs) {} + virtual void View() const; +}; + +class lacksDMA : public ABC +{ +private: + enum + { + COL_LEN = 40 + }; + char color[COL_LEN]; + +public: + lacksDMA(const char *l = "null", int r = 0, const char *c = "blank"); + lacksDMA(const ABC &rs, const char *c = "blank"); + friend ostream &operator<<(ostream &os, const lacksDMA &ls); + virtual void View() const; +}; + +class hasDMA : public ABC +{ +private: + char *style; +public: + hasDMA(const char *l = "null", int r = 0, const char *s = "none"); + hasDMA(const ABC &rs, const char *s); + hasDMA(const hasDMA &hs); + ~hasDMA(); + hasDMA &operator=(const hasDMA &hs); + friend ostream &operator<<(ostream &os, const hasDMA &hs); + virtual void View() const; +}; + +#endif \ No newline at end of file diff --git a/13-class_inheritance/exercises/p3/usedma.cpp b/13-class_inheritance/exercises/p3/usedma.cpp new file mode 100644 index 0000000..537aee9 --- /dev/null +++ b/13-class_inheritance/exercises/p3/usedma.cpp @@ -0,0 +1,34 @@ +#include "dma.h" + +using namespace std; + +int main() +{ + baseDMA shirt("Protablly", 8); + cout << "Displaying baseDMA object: " << endl; + cout << shirt; + cout << "--------------------------------------" << endl; + + lacksDMA ballon("Blimpo", 4, "red"); + cout << "Displaying lacksDMA object: " << endl; + cout << ballon; + cout << "--------------------------------------" << endl; + + lacksDMA ballon2(ballon); + cout << "Result of lacksDMA copy: " << endl; + cout << ballon2; + cout << "--------------------------------------" << endl; + + hasDMA map("keys", 5, "Mercator"); + cout << "Display hasDMA object: " << endl; + cout << map; + cout << "--------------------------------------" << endl; + + hasDMA map2 = map; // initialization: call Copy construct function + // hasDMA map2; + // map2 = map; // assign: call Operator function + cout << "Result of hasDM copy: " << endl; + cout << map2; + + return 0; +} \ No newline at end of file diff --git a/13-class_inheritance/exercises/p4/bop.cpp b/13-class_inheritance/exercises/p4/bop.cpp new file mode 100644 index 0000000..783bd97 --- /dev/null +++ b/13-class_inheritance/exercises/p4/bop.cpp @@ -0,0 +1,110 @@ +#include "bop.h" +#include + +Port::Port(const char *br, const char *st, int b) +{ + brand = new char[strlen(br)+1]; + strcpy(brand, br); + strcpy(style, st); + if (strlen(st) >= 20) + style[19] = '\0'; + else + style[strlen(st)] = '\0'; + bottles = b; +} + +Port::Port(const Port &p) +{ + brand = new char[strlen(p.brand)+1]; + strcpy(brand, p.brand); + strcpy(style, p.style); + if (strlen(p.style) >= 20) + style[19] = '\0'; + else + style[strlen(p.style)] = '\0'; + bottles = p.bottles; +} + +Port &Port::operator=(const Port &p) +{ + if (this == &p) + return *this; + delete [] brand; + brand = new char[strlen(p.brand)+1]; + strcpy(style, p.style); + if (strlen(p.style) >= 20) + style[19] = '\0'; + else + style[strlen(p.style)] = '\0'; + bottles = p.bottles; + return *this; +} + +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 << "Kind: " << style << endl; + cout << "Bottles: " << bottles << endl; +} + +ostream &operator<<(ostream &os, const Port &p) +{ + os << p.brand << ", " << p.style << ", " << p.bottles << endl; + return os; +} + +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) +{ + nickname = new char[strlen(nn)+1]; + strcpy(nickname, nn); + year = y; +} + +VintagePort::VintagePort(const VintagePort &vp) +{ + nickname = new char[strlen(vp.nickname)+1]; + strcpy(nickname, vp.nickname); + year = vp.year; +} + +VintagePort &VintagePort::operator=(const VintagePort &vp) +{ + if (this == &vp) + return *this; + delete[] nickname; + nickname = new char[strlen(vp.nickname)+1]; + 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; + return os; +} \ No newline at end of file diff --git a/13-class_inheritance/exercises/p4/bop.h b/13-class_inheritance/exercises/p4/bop.h new file mode 100644 index 0000000..f71c6f4 --- /dev/null +++ b/13-class_inheritance/exercises/p4/bop.h @@ -0,0 +1,40 @@ +#ifndef __BOP_H__ +#define __BOP_H__ + +#include +using namespace std; + +class Port +{ +private: + char * brand; + char style[20]; // i.e., tawny, ruby, vintage + int bottles; +public: + Port(const char *br = "none", const char *st = "none", int b = 0); + Port(const Port &p); // copy constructor + virtual ~Port() {delete [] brand;} + Port &operator=(const Port &p); + Port &operator+=(int b); // adds b to bottles + Port &operator-=(int b); // subtracts b from bottles, if availabe + int BottleCount() const {return bottles;} + virtual void Show() const; + friend ostream &operator<<(ostream &os, const Port &p); +}; + +class VintagePort : public Port // style necessarily = "vintage" +{ +private: + char* nickname; // i.e., "The Noble" or "Old Velvet", etc. + int year; // vintage 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); +}; + +#endif \ No newline at end of file diff --git a/13-class_inheritance/exercises/p4/main.cpp b/13-class_inheritance/exercises/p4/main.cpp new file mode 100644 index 0000000..d998e02 --- /dev/null +++ b/13-class_inheritance/exercises/p4/main.cpp @@ -0,0 +1,29 @@ +#include +#include "bop.h" + +int main() +{ + + Port port1("ABC", "sweet", 200); + cout << port1 << endl; + cout << "--------------------------" << endl; + + VintagePort vp("EFG", "vintage", 300, "Old Jack", 40); + cout << vp << endl; + cout << "--------------------------" << endl; + + VintagePort vp2(vp); + cout << vp2 << endl; + cout << "--------------------------" << endl; + + Port *p_port; + p_port = &port1; + p_port->Show(); + cout << "--------------------------" << endl; + + p_port = &vp; + p_port->Show(); + cout << "--------------------------" << endl; + + return 0; +} \ No newline at end of file -- Gitee From ffa2b1168e4e4d8d5dec3bb39a4e8cc1f367fd91 Mon Sep 17 00:00:00 2001 From: fyang Date: Mon, 6 Jun 2022 22:32:15 +0800 Subject: [PATCH 4/5] Add exercies code for chapter -- 14 --- 14-reusing_code_in_cpp/student/studentc.h | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 14-reusing_code_in_cpp/student/studentc.h diff --git a/14-reusing_code_in_cpp/student/studentc.h b/14-reusing_code_in_cpp/student/studentc.h new file mode 100644 index 0000000..157c577 --- /dev/null +++ b/14-reusing_code_in_cpp/student/studentc.h @@ -0,0 +1,33 @@ +#ifndef __STUDENTC_H__ +#define __STUDENTC_H__ + +#include +#include +#include +using namespace std; + +class Student +{ +private: + typedef valarray ArrayDb; + string name; + ArrayDb score; // valarray scores; +public: + Student() : name("Null Student"), score() {} + explicit Student(const std::string &s) : name(s), score() {} + explicit Student(int n) : name("Nully"), score(n) {} + Student(const string &s, int n) : name(s), score(n) {} + Student(const string &s, const ArrayDB &a) : name(s), score(a) {} + Student(const string &s, const double *pd, int n) : name(s), score(pd, n) {} + ~Student() {} + double Average() const; + const string &Name() const; + double &operator[](int n); // stu[0] = 100; + double operator[](int n) const; // a = stu[0]; + + friend istream &operator>>(istream &is, Student &stu); + friend istream &getline(istream &is, Student &stu); + friend ostream &operator<<(ostream &os, const Student &stu); +}; + +#endif \ No newline at end of file -- Gitee From e6ad4fed2b4479ab6668c03e7f1b0f85421f4461 Mon Sep 17 00:00:00 2001 From: fyang Date: Mon, 6 Jun 2022 23:06:29 +0800 Subject: [PATCH 5/5] Add exercises.md --- 13-class_inheritance/exercises/exercises.md | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/13-class_inheritance/exercises/exercises.md b/13-class_inheritance/exercises/exercises.md index e69de29..277008d 100644 --- a/13-class_inheritance/exercises/exercises.md +++ b/13-class_inheritance/exercises/exercises.md @@ -0,0 +1,32 @@ +### 复习题 +#### 1. 派生类从基类那里继承了什么? + +**解析:** +在类的继承和派生中, C++ 中的派生类能够继承基类的所有数据成员和大部分成员函数。但是基类中不同访问控制权限的成员在派生类中的访问权限也不相同。公有成员直接成为派生类的公有成员,派生类的对象可以直接访问;基类的保护成员成为派生类的保护成员;基类的私有成员被派生类继承,但派生类不能直接访问,只能通过基类的公有方法间接访问。 + +#### 2. 派生类不能从基类那里继承什么? + +**解析:** + +C++ 的继承过程中,派生类能够从基类继承所有数据成员和大部分成员函数,但是基类的构造函数、析构函数、赋值运算符、友元函数和友元类不能继承。基类的构造函数能够在派生类的构造过程中默认调用,也可以通过派生类的初始化列表显式调用。在派生类对象的析构过程中,系统先调用派生类的析构函数,后调用基类的析构函数。 + +#### 3. 假设 `baseDMA::operator=()` 函数的返回值类型为 `void`,而不是 `baseDMA &`,将会产生什么后果?如果返回类型为 `baseDMA`,而不是 `baseDMA &`,又将有什么后果? + +**解析:** + +如果赋值运算符在重载中返回值的类型为 `void`, 仍可以使用单个赋值,但不能使用连锁赋值,可以使用 `baseDMA a = b`, 但不能使用 `baseDMA a = b = c`。 + +因为 `b = c` 的返回结果为 `void`。如果赋值运算符返回一个对象,而不是引用,则该方法在执行过程中会进行函数的返回值向被赋值对象的赋值过程,因此运行效率将会降低。 + +#### 4. 在创建和删除派生类对象时,构造函数和析构函数的调用顺序是怎样的? + +**解析:** + +类的继承过程中,派生类不会继承基类的构造函数和析构函数,但是派生类的对象在创建过程中会先调用基类的构造函数,创建基类对象,然后以此类推最后调用派生类的构造函数。析构过程正好相反,先调用派生类的析构函数,再依次调用基类的析构函数,最后完成对象的销毁。 + +#### 5. 如果没有为派生类添加任何数据成员,它是否需要构造函数? + +**解析:** + +C++ 语言中每个类都必须有自己的构造函数。即使没有数据成员和自定义的构造函数,系统也会自动生成一个默认构造函数,并在创建对象时调用该构造函数。 + \ No newline at end of file -- Gitee