Files
OOP-Cpp/oop_hw5/hw4/题目以及答案文字部分.md
2025-08-11 00:01:30 +08:00

346 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2025-05-12 13:19:46
* @LastEditors: error: git config user.name & please set dead value or install git
* @LastEditTime: 2025-05-12 14:10:25
-->
## 4.阅读代码,并按要求练习。
``` cpp
class A
{
public:
A(int num):data1(num) {}
~A(){
cout<<" Destory A"<<endl;
}
void f() const{
cout<<" Excute A::f() ";
cout<<" Data1="<<data1<<endl;
}
void g()
{
cout<<" Excute A::g() "<<endl;
}
private:
int data1;
};
class B : public A
{
public:
B(int num1,int num2):A(num1),data2(num2) {}
~B(){
cout<<" Destory B"<<endl;
}
void f( ) const{
cout<<" Excute B::f() ";
cout<<" Data1="<< data1;
cout<<" Data2="<<data2<<endl;
}
void f(int n) const{
cout<<" Excute B::f(int) ";
cout<<" n="<<n;
cout<<" Data1="<< data1;
cout<<" Data2="<<data2<<endl;
}
void h(){
cout<<" Excute B::h() "<<endl;
}
private:
int data2;
};
```
## 下面是问题
- ##### 1)完成 B 类的构造函数,使得参数 num1 对应 data1num2 对应 data2
✅已完成:
```cpp
B(int num1, int num2) : A(num1), data2(num2) {}
```
这正确地将 `num1` 传递给基类 A`num2` 初始化 `data2`。✅
---
- ##### 2)尝试在 main 函数中使用这两个类,编译程序看是否有编译错误?指出错误的原因。
✅已完成:
```cpp
B b(1, 2);
b.f();
b.g();
b.f(3);
b.h();
```
若此时 A 中 `data1` 是 `private`,则:
* ✅ `b.f()`、`b.g()`、`b.f(3)`、`b.h()` 都能编译通过。
* ⚠️ 但 `B::f()` 中访问 `data1` 会报错(因为 `B` 不能访问 `A::data1` 的 `private` 成员)。
---
- ##### 3)将基类中的 private 改为 protected再编译。理解 protected 访问权限在public 继承方式下的可访问性。
✅ 将 A 的成员 `private → protected` 后
* `B` 就可以访问 `A::data1`。
* 所以 `B::f()` 和 `f(int)` 中访问 `data1` 合法。
* `protected` 在 `public` 继承下,**对子类可见,对外仍然不可见**。
---
- ##### 4)修改 main 函数,如下所示,看看哪些语句合法?为什么?执行的是基类的实现,还是派生类的实现?
``` cpp
int main()
{
B b(1,2);
b.f();
b.g();
b.f(3);
b.h();
return 0;
}
```
✅已完成:
```cpp
int main() {
B b(1, 2);
b.f(); // ✅ 调用 B::f()
b.g(); // ✅ 调用 A::g()
b.f(3); // ✅ 调用 B::f(int)
b.h(); // ✅ 调用 B::h()
}
```
**全部合法**,执行的都是定义在类中的相应成员函数,优先使用派生类的版本。
---
- ##### 5)将继承 A 类的继承方式改为 private编译能通过吗再执行 4)中的main 函数,看看哪些语句变得不合法了?为什么?
✅ 改为 `private` 继承后:
```cpp
class B : private A
```
现在,`A` 的成员都变成 `B` 的私有成员。
* `main()` 中 `b.g();` 不合法(`A::g()` 是 `private` 了)
* `b.f();` 也不合法,因为 `A::f()` 被隐藏
* ❌ **除了 `b.f(3)` 和 `b.h()` 还行,其余都报错**
---
- ##### 6)将继承 A 类的继承方式改回 public并实现 B 类自定义的拷贝构造和赋值函数。
✅已构造A类的operator+
```cpp
class B : public A {
// ...
public:
B(const B& other) : A(other), data2(other.data2) {}
B& operator=(const B& other) {
if (this != &other) {
A::operator=(other);
//或者
this->A::operator=(other);
//这种写法也可以
data2 = other.data2;
}
return *this;
}
};
```
---
- ##### 7)分别创建 A 和 B 类的两个对象 a 和 b分别执行 a.f()b.f()a.g()b.g()a.f(1)b.f(1)a.h()b.h(),请问哪些可以通过编译,执行结果如何?
✅ 调用:
```cpp
A a(1);
B b(2, 3);
a.f(); // ✅ A::f()
b.f(); // ✅ B::f()
a.g(); // ✅ A::g()
b.g(); // ✅ A::g() 继承而来
a.f(1); // ❌ 无此函数
b.f(1); // ✅ B::f(int)
a.h(); // ❌ 无此函数
b.h(); // ✅ B::h()
```
---
- ##### 8)增加代码 A * p=new B(1,2);,理解向上类型转换的安全性。
✅已完成:
```cpp
A* p = new B(1, 2);
```
✅ 安全称为“向上转型”upcasting**B 是 A 的子类**。安全的主要原因:
“向上类型转换的安全性”是面向对象编程中一个很关键但也容易被忽略的点,尤其在 C++ 中。
---
## ✅ 什么是向上类型转换Upcasting
向上类型转换:**将派生类对象的指针或引用转换为基类类型的指针或引用**。
比如:
```cpp
class A { };
class B : public A { };
B b;
A* pa = &b; // 向上类型转换Upcasting
```
---
## ✅ 安全性的本质
### ✅ **向上转换是安全的、隐式允许的**
因为:
* 每个派生类对象**本质上包含一个完整的基类子对象部分**。
* 所以把 `B*` 转为 `A*` 是有意义的:`A` 的部分总是存在的。
> 换句话说,你永远可以“安全地把一个更具体的对象视为一个更抽象的对象”。
---
## ✅ 示例
```cpp
class A {
public:
void foo() { cout << "A::foo\n"; }
};
class B : public A {
public:
void bar() { cout << "B::bar\n"; }
};
int main() {
B b;
A* pa = &b; // 向上转换,安全
pa->foo(); // ✅合法,调用 A 的函数
// pa->bar(); // ❌错误A 类型中没有 bar()
}
```
---
## ❗ 注意事项:类型“退化”
向上转换后:
* **只能访问基类中的成员**,即便实际指向的是一个派生类对象;
* 若函数是非虚函数,则会“静态绑定”到基类版本;
* 若函数是虚函数,则会“动态绑定”到派生类重写版本(见多态);
---
## ✅ 与向下转换对比
| 类型转换方式 | 安全性 | 是否隐式 | 风险 |
| --------- | ----- | ---- | ---------------- |
| 向上转换B→A | ✅ 安全 | ✅ 是 | 无 |
| 向下转换A→B | ❌ 不安全 | ❌ 否 | 若对象本非 B 类型,结果未定义 |
---
- ##### 9)在 8)的基础上,执行 p->f(),输出是什么?与 B* p=new B(1,2); p->f();的结果一样吗?
✅执行后:
默认情况下(`f()` 非虚函数):
```cpp
p->f(); // 输出 A::f()
```
若你希望执行的是 `B::f()`**应将 A 的 `f()` 声明为 `virtual`**。
---
- ##### 10)在 8)的基础上,执行 p->f(1),能通过编译吗?为什么?
✅编译?
```cpp
p->f(1); // ❌ 报错A 类中无 f(int)
```
即使实际对象是 B编译器只看指针类型A而 A 没有 `f(int)`。
---
- ##### 11)在 8)的基础上,执行 p->g()和 p->h(),能行吗?为什么?
✅编译?
```cpp
p->f(1); // ❌ 报错A 类中无 f(int)
```
即使实际对象是 B编译器只看指针类型A而 A 没有 `f(int)`。
---
- ##### 12)在 8)的基础上,执行 delete p;输出是什么B 类的析构函数执行了吗?
✅输出:
如果 `A` 的析构函数不是 `virtual`,那么:
```cpp
delete p; // 只会调用 A 的析构函数,❌ 不会调用 B 的析构函数
```
输出:
```
Destory A
```
⚠️ 此时会发生**资源泄漏**,因为 `B` 中资源未释放!
✅ 正确写法是:
```cpp
class A {
public:
virtual ~A() {
cout << "Destory A" << endl;
}
// ...
};
```
这样 `delete p;` 才会调用 B 的析构函数 → 输出顺序:
```
Destory B
Destory A
```
---
## ✅ 总结重点
| 问题 | 核心知识 |
| ---- | -------------------------- |
| 23 | 访问权限private vs protected |
| 45 | 继承方式public/private影响成员访问 |
| 6 | 拷贝构造函数 & 赋值操作符 |
| 711 | 多态、向上转型、成员函数隐藏 |
| 12 | 虚析构函数,资源释放 |
---