C++——多态2|virtual与析构函数|C++11override 和 final|重载,重写(覆盖),隐藏(重定义对比| 抽象类|子类和父类虚表|多继承|习题|总结(下)

简介: 笔记

习题


using namespace std;
class A{
public:
A(char *s) { cout<<s<<endl; }
~A(){}
};
class B:virtual public A
{
public:
B(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class C:virtual public A
{
public:
C(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class D:public B,public C
{
public:
D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3),A(s1)
{ cout<<s4<<endl;}
};
int main() {
D *p=new D("class A","class B","class C","class D");
delete p;
return 0;
}


A:class A class B class C class D B:class D class B class C class A

C:class D class C class B class A D:class A class C class B class D


子类必须调用父类的构造函数,这里不调用A的构造函数会报错,因为是虚继承,A是B和C共享的,但是B和C都不能初始化A,此时应该由D去初始化A,因为D里面的A只有一份,而且这个A是B和C共享的。


注意B和C里面都有对A构造


调用顺序:编译器会特殊处理,不会重复对A进行构造,D里面只有一份A,只在D里面初始化,编译器按照声明的顺序进行初始化,所以先初始化A,然后B,C,D。


B和C初始化A的意义,可能会单独定义B或C对象。


多态总结


 inline函数可以是虚函数吗?(内联函数是直接展开,内联函数没有地址,汇编时候不进行call操作,虚函数要放进虚函数表,虚函数表中填的是函数地址)


答:可以,不过编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去。


static函数可以是虚函数吗


首先测试一下不行,编译器直接报错,因为静态成员函数没有this指针,直接使用A::FUNC2()类域指定的形式调用,虚函数是为了实现多态,多态都是运行时直接去虚表中决议(寻找),static成员函数都是在编译时决议(直接call函数地址,不通过虚表),所以静态函数不能成为虚函数


构造函数可以是虚函数吗


运行程序直接报错,对创建好后,对象没有被初始化,虚表也没有被初始化

1.png

在初始化列表阶段会对对象和虚表进行初始化(找到虚表的地址再初始化)

2.png



虚函数是为了实现多态,运行时去虚表找对应的虚函数进行调用,对象中虚表指针都是构造函数初始化列表阶段才初始化,构造函数是虚函数是没有意义的


析构函数可以是虚函数吗


可以,建议基类的析构函数定义成虚函数,博客里有提到过。


拷贝构造和operator=可不可以是虚函数?


拷贝构造不可以,拷贝构造也是构造函数,拷贝构造也有初始化列表。


operator=:可以,子类B的赋值不是A类赋值的重写,因为参数不同,但这种重写可以让子类对象接收父类对象


class A
{
public:
  virtual A& operator=(const A& aa)
  {
  return *this;
  }
};
class B:public  A
{
public:
  virtual B& operator=(const B& aa)
  {
  return *this;
  }
};

这种赋值重写,可以把父类赋值给子类

3.png

但如果去掉赋值重写,这种写法就会报错,因为这是C++的语法规定

4.png



对象访问普通函数快还是虚函数快


首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。



虚函数表是在什么阶段生成的,存在哪的?


虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。


构造函数初始化列表阶段初始化的是虚函数表指针,对象中存的也虚函数表指针


习题


1.关于重载、重写和重定义的区别说法正确的是( A F)


A.重写和重定义都发生在继承体系中


B.重载既可以在一个类中,也可以在继承体系中


C.它们都要求原型相同


D.重写就是重定义


E.重定义就是重写


F.重写比重定义条件更严格


G.以上说法全错误


A.重写即覆盖,针对多态, 重定义即隐藏, 两者都发生在继承体系中


B.重载只能在一个范围内,不能在不同的类里


C.只有重写要求原型相同


D.重写和重定义是两码事,重写即覆盖,针对多态, 重定义即隐藏


E.重写和重定义是两码事,重写即覆盖,针对多态, 重定义即隐藏


F.重写要求函数完全相同,重定义只需函数名相同即可


G.很明显有说法正确的答案


2.关于重载和多态正确的是 ( B)


A.如果父类和子类都有相同的方法,参数个数不同, 将子类对象赋给父类对象后, 采用父类对象调用该同名方法时,实际调用的是子类的方法


B.选项全部都不正确


C.重载和多态在C++面向对象编程中经常用到的方法,都只在实现子类的方法时才会使用


D.class A{ public: void test(float a) { cout << a; } }; class B :public A{ public: void test(int b){ cout << b; } }; void main() { A *a = new A; B *b = new B; a = b; a->test(1.1); } 结果是1


A.使用父类对象调用的方法永远是父类的方法


B.正确


C.重载不涉及子类


D.输入结果为1.1


3.以下哪项说法时正确的(D )


class A
{
public:
void f1(){cout<<"A::f1()"<<endl;}
virtual void f2(){cout<<"A::f2()"<<endl;}
virtual void f3(){cout<<"A::f3()"<<endl;}
};
class B : public A
{
public:
virtual void f1(){cout<<"B::f1()"<<endl;}
virtual void f2(){cout<<"B::f2()"<<endl;}
void f3(){cout<<"B::f3()"<<endl;}
};

A.基类和子类的f1函数构成重写


B.基类和子类的f3函数没有构成重写,因为子类f3前没有增加virtual关键字


C.如果基类指针引用子类对象后,通过基类对象调用f2时,调用的是子类的f2


D.f2和f3都是重写,f1是重定义


4.关于抽象类和纯虚函数的描述中,错误的是 ( D)


A.纯虚函数的声明以“=0;”结束


B.有纯虚函数的类叫抽象类,它不能用来定义对象


C.抽象类的派生类如果不实现纯虚函数,它也是抽象类


D.纯虚函数不能有函数体


A.纯虚函数的声明以“=0;”结束,这是语法要求


B.有纯虚函数的类叫抽象类,它不能用来定义对象,一般用于接口的定义


C.子类不实现父类所有的纯虚函数,则子类还属于抽象类,仍然不能实例化对象


D.纯虚函数可以有函数体,只是意义不大


5.假设A为抽象类,下列声明( )是正确的


A.A fun(int);


B.A*p;


C.int fun(A);


D.A obj;


A.抽象类不能实例化对象,所以以对象返回是错误


B.抽象类可以定义指针,而且经常这样做,其目的就是用父类指针指向子类从而实现多态


C.参数为对象,所以错误


D.直接实例化对象,这是不允许的

相关文章
|
2月前
|
安全 Java 编译器
C++进阶(1)——继承
本文系统讲解C++继承机制,涵盖继承定义、访问限定符、派生类默认成员函数、菱形虚拟继承原理及组合与继承对比,深入剖析其在代码复用与面向对象设计中的应用。
|
6月前
|
存储 安全 Java
c++--继承
c++作为面向对象的语言三大特点其中之一就是继承,那么继承到底有何奥妙呢?继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用,继承就是类方法的复用。
148 0
|
9月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
477 6
|
11月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
327 5
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
172 0
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
262 0
|
8月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
304 12
|
9月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
180 16
|
10月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)