Files
OOP-Cpp/oop_hw6/hw1/QA1.md
2025-08-11 00:01:30 +08:00

4.2 KiB
Raw Permalink Blame History

1去掉(1)中的 virtual,比较执行结果。

virtual ~A() {...}

如果去掉 virtual,在通过 A* p = new B; 创建对象并用 delete p; 释放时:

  • 原代码:会先调用 B::~B(),再调用 A::~A()(因为析构函数是虚函数,发生动态绑定,析构顺序正确)。
  • 去掉 virtual:只会调用 A::~A(),不会调用 B::~B()可能导致子类资源未释放,产生内存泄漏或行为异常

结论:基类应将析构函数设为 virtual,以确保通过基类指针删除时能调用子类析构函数。


2去掉(6)中的 const,可以吗?

virtual int Func1() const;

不可以。因为你在基类 A 中定义的是:

virtual int Func1() const = 0;

这是一个纯虚函数,要求派生类重写时函数签名必须完全一致。如果你去掉 const,则签名不同,不会重写该函数,导致类 B 仍然是抽象类,不能实例化。


3(3) 和 (7) 中各自定义了参数的缺省值,(12) 执行时匹配的是哪个缺省值?为什么?

// A 类中:
virtual void Func2(int = 500) = 0;

// B 类中:
virtual void Func2(int = 1000)

语句 (12) 是:

p->Func2(); // 没传参数

此时 pA* 类型。函数的缺省参数在编译时根据指针静态类型决定,所以用的是 A::Func2(int=500) 中的默认值。

结论

  • 缺省参数值是静态绑定的,跟对象的动态类型无关。
  • 编译时看的是指针类型 A*,所以用了 500 而不是 1000

4(4) 中的 protected 改为 private 可以吗?

protected:
    static int lineno;

如果改为 private派生类 B 就不能访问 lineno,会导致编译错误(如构造函数中 ++lineno)。

结论:基类希望子类访问成员变量,应使用 protectedprivate 不可被子类访问。


5去掉 (5) 中的 virtual,对结果有影响吗?

virtual ~B() { ... }

如果 ~A() 是虚函数,那么即使 ~B() 不是虚函数,也会通过动态绑定调用 ~B()

结论:去掉 virtual 不影响结果(只要基类析构函数是虚函数),但建议保留以提高可读性和一致性。


6(9) 的作用是什么?

int A::lineno = 0;

这是静态成员变量 lineno 的定义和初始化,必须在类外初始化,否则会链接错误。

作用:为类的所有对象共享一份 lineno 计数器,用来输出行号。


7(10) 中改为 A* p = new A; 可以吗?

A * p = new A; // 错误

A 是抽象类(包含纯虚函数),不能实例化。会导致编译错误。

结论:抽象类不能直接创建对象,只能用于指针或引用指向派生类。


8去掉(3)中的 virtual,结果会有什么改变?

void Func2(int = 500) = 0; // 去掉 virtual -> 变成普通纯函数?

这在语义上是错误的。纯虚函数必须是虚函数,否则语法不合法。

结论:不能去掉,纯虚函数必须是 virtual


9(14) 为什么不对?将 (8) 改为 virtual 后,(14) 可以了吗?

// (14) p->Func3(100);

pA*,而 Func3() 并未在 A 中声明,因此不能通过 A* 调用,即使 Func3()virtual

结论

  • 静态类型决定能否调用某函数。
  • 即使改成虚函数,也需要 A 中声明它,才能通过 A* 调用。

10(15) 为什么不对?理解编译时静态类型和运行时动态类型的含义。

// p->Func1(100); // 错误

B 中有两个重载版本:

int Func1(int n) const;
int Func1() const;

A* p 只能看到 A::Func1() const 版本(无参数)。你尝试调用的是 Func1(int),这在 A 中根本没有定义,编译时报错。

结论

  • 编译时由静态类型决定调用哪个函数(是否存在该函数签名)。
  • 运行时由动态类型决定调用哪个版本(多态行为)。
  • 所以 p->Func1(100) 不合法,因为 A 中没这个函数签名。