OOP HomeWork
This commit is contained in:
156
oop_hw6/hw1/QA1.md
Normal file
156
oop_hw6/hw1/QA1.md
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
|
||||
### **1)去掉(1)中的 `virtual`,比较执行结果。**
|
||||
|
||||
```cpp
|
||||
virtual ~A() {...}
|
||||
```
|
||||
|
||||
如果去掉 `virtual`,在通过 `A* p = new B;` 创建对象并用 `delete p;` 释放时:
|
||||
|
||||
* **原代码**:会先调用 `B::~B()`,再调用 `A::~A()`(因为析构函数是虚函数,发生动态绑定,析构顺序正确)。
|
||||
* **去掉 `virtual` 后**:只会调用 `A::~A()`,不会调用 `B::~B()`,**可能导致子类资源未释放,产生内存泄漏或行为异常**。
|
||||
|
||||
✅ **结论**:基类应将析构函数设为 `virtual`,以确保通过基类指针删除时能调用子类析构函数。
|
||||
|
||||
---
|
||||
|
||||
### **2)去掉(6)中的 `const`,可以吗?**
|
||||
|
||||
```cpp
|
||||
virtual int Func1() const;
|
||||
```
|
||||
|
||||
不可以。因为你在基类 A 中定义的是:
|
||||
|
||||
```cpp
|
||||
virtual int Func1() const = 0;
|
||||
```
|
||||
|
||||
这是一个**纯虚函数**,要求派生类重写时函数签名必须完全一致。如果你去掉 `const`,则签名不同,**不会重写该函数**,导致类 `B` 仍然是抽象类,不能实例化。
|
||||
|
||||
---
|
||||
|
||||
### **3)(3) 和 (7) 中各自定义了参数的缺省值,(12) 执行时匹配的是哪个缺省值?为什么?**
|
||||
|
||||
```cpp
|
||||
// A 类中:
|
||||
virtual void Func2(int = 500) = 0;
|
||||
|
||||
// B 类中:
|
||||
virtual void Func2(int = 1000)
|
||||
```
|
||||
|
||||
语句 `(12)` 是:
|
||||
|
||||
```cpp
|
||||
p->Func2(); // 没传参数
|
||||
```
|
||||
|
||||
此时 `p` 是 `A*` 类型。**函数的缺省参数在编译时根据指针静态类型决定**,所以用的是 `A::Func2(int=500)` 中的默认值。
|
||||
|
||||
✅ **结论**:
|
||||
|
||||
* 缺省参数值是静态绑定的,跟对象的动态类型无关。
|
||||
* 编译时看的是指针类型 `A*`,所以用了 `500` 而不是 `1000`。
|
||||
|
||||
---
|
||||
|
||||
### **4)(4) 中的 `protected` 改为 `private` 可以吗?**
|
||||
|
||||
```cpp
|
||||
protected:
|
||||
static int lineno;
|
||||
```
|
||||
|
||||
如果改为 `private`,**派生类 `B` 就不能访问 `lineno`,会导致编译错误**(如构造函数中 `++lineno`)。
|
||||
|
||||
✅ **结论**:基类希望子类访问成员变量,应使用 `protected`;`private` 不可被子类访问。
|
||||
|
||||
---
|
||||
|
||||
### **5)去掉 (5) 中的 `virtual`,对结果有影响吗?**
|
||||
|
||||
```cpp
|
||||
virtual ~B() { ... }
|
||||
```
|
||||
|
||||
如果 `~A()` 是虚函数,那么即使 `~B()` 不是虚函数,也会通过动态绑定调用 `~B()`。
|
||||
|
||||
✅ **结论**:去掉 `virtual` 不影响结果(只要基类析构函数是虚函数),但建议保留以提高可读性和一致性。
|
||||
|
||||
---
|
||||
|
||||
### **6)(9) 的作用是什么?**
|
||||
|
||||
```cpp
|
||||
int A::lineno = 0;
|
||||
```
|
||||
|
||||
这是静态成员变量 `lineno` 的定义和初始化,**必须在类外初始化**,否则会链接错误。
|
||||
|
||||
✅ **作用**:为类的所有对象共享一份 `lineno` 计数器,用来输出行号。
|
||||
|
||||
---
|
||||
|
||||
### **7)(10) 中改为 `A* p = new A;` 可以吗?**
|
||||
|
||||
```cpp
|
||||
A * p = new A; // 错误
|
||||
```
|
||||
|
||||
`A` 是抽象类(包含纯虚函数),**不能实例化**。会导致编译错误。
|
||||
|
||||
✅ **结论**:抽象类不能直接创建对象,只能用于指针或引用指向派生类。
|
||||
|
||||
---
|
||||
|
||||
### **8)去掉(3)中的 `virtual`,结果会有什么改变?**
|
||||
|
||||
```cpp
|
||||
void Func2(int = 500) = 0; // 去掉 virtual -> 变成普通纯函数?
|
||||
```
|
||||
|
||||
这在语义上是错误的。**纯虚函数必须是虚函数**,否则语法不合法。
|
||||
|
||||
✅ **结论**:不能去掉,纯虚函数必须是 `virtual`。
|
||||
|
||||
---
|
||||
|
||||
### **9)(14) 为什么不对?将 (8) 改为 `virtual` 后,(14) 可以了吗?**
|
||||
|
||||
```cpp
|
||||
// (14) p->Func3(100);
|
||||
```
|
||||
|
||||
`p` 是 `A*`,而 `Func3()` 并未在 `A` 中声明,因此不能通过 `A*` 调用,即使 `Func3()` 是 `virtual`。
|
||||
|
||||
✅ **结论**:
|
||||
|
||||
* 静态类型决定能否调用某函数。
|
||||
* 即使改成虚函数,也需要 `A` 中声明它,才能通过 `A*` 调用。
|
||||
|
||||
---
|
||||
|
||||
### **10)(15) 为什么不对?理解编译时静态类型和运行时动态类型的含义。**
|
||||
|
||||
```cpp
|
||||
// p->Func1(100); // 错误
|
||||
```
|
||||
|
||||
在 `B` 中有两个重载版本:
|
||||
|
||||
```cpp
|
||||
int Func1(int n) const;
|
||||
int Func1() const;
|
||||
```
|
||||
|
||||
但 `A* p` 只能看到 `A::Func1() const` 版本(无参数)。你尝试调用的是 `Func1(int)`,这在 `A` 中根本没有定义,编译时报错。
|
||||
|
||||
✅ **结论**:
|
||||
|
||||
* 编译时由静态类型决定调用哪个函数(是否存在该函数签名)。
|
||||
* 运行时由动态类型决定调用哪个版本(多态行为)。
|
||||
* 所以 `p->Func1(100)` 不合法,因为 `A` 中没这个函数签名。
|
||||
|
||||
|
Reference in New Issue
Block a user