面试题-面向对象篇

面向对象问题汇总

类的大小

Q:一个只含有虚函数的类的size为多少?
A:参考这个博客
#类中的元素
0. 成员变量 1. 成员函数 2. 静态成员变量 3. 静态成员函数 4. 虚函数 5. 纯虚函数
#影响对象大小的因素
0. 成员变量 1. 虚函数表指针(_vftptr) 2. 虚基类表指针(_vbtptr) 3. 内存对齐
**什么都没有,占用一个字节**sizeof(空类的实例) = 1 byte ,如果有其他的,这个字节就不用算了。
类函数不占用空间,加了构造函数和析构函数,sizeof(实例) = 1byte
有虚函数,实例中需要添加一个虚函数表的指针 sizeof(实例) = 4 byte
数据成员占用空间 int4 char1 注意要对齐

Q:含虚函数类的虚函数表是存放在哪里的
C++虚函数的具体实现原理(虚函数和虚函数表是热门问题,一定要弄清楚其原理与实现)
实现编译器处理虚函数表应该如何处理
A:
虚函数表vtbl实际上是一个指针数组,存放了虚函数的地址。指向虚函数表的指针vptr存在对象实例的最前面的位置。
虚函数指针是在构造函数执行时初始化的,虚函数表存放在了目标文件或者可执行文件的常量段中。虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata)
虚表在静态区,因为对每个类,所有对象共用虚表。

Q:C++的构造函数可以为虚函数吗?析构函数一般写成虚函数的原因
A:
构造函数不能为虚函数,而析构函数可以且常常是虚函数。
C++对象的构造,C++对象在三个地方构建:(1)函数堆栈;(2)自由存储区,或称之为堆;(3)静态存储区。
无论在那里构建,过程都是两步:首先,分配一块内存;其次,调用构造函数。
虚函数需要通过vtable来调用,而vtable是在构造函数中初始化的,所以构造函数不能为虚函数。
析构函数需要写成虚函数
防止只析构基类而不析构派生类的状况发生,想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的

Q:什么情况下要给类写拷贝构造函数
A:
三种情况下会调用拷贝构造函数:
1) 一个对象作为函数参数,以值传递的方式传入函数体;
2) 一个对象作为函数返回值,以值传递的方式从函数返回;
3) 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
需要定义拷贝构造函数
如果你需要定义一个非空的析构函数,那么,通常情况下你也需要定义一个拷贝构造函数。
①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载”=”赋值操作符号。

Q:为什么用成员初始化列表会快一些
A:
foo(string s, int i):name(s), id(i){}
省去了临时对象的存在,C++的赋值操作是会产生临时对象的。对内置类型没有多大差别。而类类型则减少了一次调用默认构造函数,直接调用拷贝构造函数。
必须用初始化列表的情况:1)类中const成员 2)其它类B作为成员,类B禁止了赋值操作

Q:struct与class的区别
A:
默认的访问控制:默认的继承访问权限(struct是public的,class是private的);默认的数据访问控制(成员变量访问控制struct是public的,class是private的)
struct更体现出一种数据结构,class更像【对象】
“class”这个关键字还用于定义模板参数,就像“typename”

Q:C++内存模型
A:
C语言:(由低地址到高地址)
a)正文段:用来存放程序执行代码。通常,正文段是可共享的。另外,正文段常常是只读的,一次防止程序由于意外修改其自身。
b)初始化数据段:用来存放程序中已初始化的全局变量。数据段属于静态内存分配。
c)非初始化数据段:通常称为BSS段, 用来存放程序中未初始化的全局变量。BSS是英文Block Started by Symbol(由符号开始的块)的简称。BSS段属于静态内存分配。 在程序开始执行之前,内核将此段中的数据初始化为0或者空指针。
d)堆:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上 (堆被扩张)/释放的内存从堆中被剔除(堆被缩减)。
e)栈:栈又称堆栈, 存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈 的先进后出特点,所以栈特别方便用来保存/恢复调用现场。
C++中,内存分成5个区,他们分别是自由存续区全局/静态存续区常量存续区
a) 栈:内存由编译器在需要时自动分配和释放。通常用来存储局部变量和函数参数。栈运算分配内置于处理器的指令集中,效率很高,但是分配的内存容量有限。在大多数C编译器中,参数是由右往左入栈
b) 堆:内存使用new进行分配使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。
堆是向高地址扩展数据结构,是不连续内存区域。空间比较大。系统是用链表来存储空闲内存地址,链表遍历方向是由低地址向高地址。会在这块内存空间中首地址处记录本次分配大小,这样,代码中delete语句才能正确释放本内存空间。
c) 自由存储区:使用malloc进行分配,使用free进行回收。和堆类似。
d) 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。(静态局部变量生存期为整个源程序,在其他函数中它是“不可见”的。)
e) 常量存储区:存储常量,不允许被修改。
Q:动态绑定
A:
多态,运行时确定地址,虚函数实现。

Q:new 和malloc的区别
1)类型不同:
malloc与free是C++/C语言的标准库函数
new/delete是C++的运算符
2)分配内存的位置不同:
malloc函数从上动态分配内存
new操作符从自由存储区(free store)上为对象动态分配内存空间
自由存储区不仅可以是堆,还可以是静态存储区
3)构造函数/析构函数
new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。
而malloc则不会。
参考链接:http://www.linuxidc.com/Linux/2016-01/127591.htm
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象程序设计》试题 一、单选题(每空2分,共40分) 1、关于C++与C语言关系的描述中,( )是错误的。 A.C语言是C++语言的一个子集 B.C语言与C++语言是兼容的 C.C++语言对C语言进行了一些改进 D.C++语言和C语言都是面向对象的 2、已知:int m=10; 下列表示引用的方法中,( )是正确的。 A.int& x=m; B.int& y=10; C.int& z; D.float& t=&m; 3、考虑下面的函数原型声明: void DefPar(int a, int b=7, char z = '*'); 下面函数调用中,不合法的是( )。 A.DefPar(5); B.DefPar(5,8); C.DefPar(5,'#'); D.DefPar(0,0,'*'); 4、系统在调用重载函数时往往根据一些条件确定哪个重载函数被调用,在下列选项中,不能作为依据的是( )。 A.函数的返回值类型 B.参数的类型 C.函数名称 D.参数个数 5、下列有关C++类的说法中,不正确的是( )。 A.类是一种用户自定义的数据类型 B.只有类中的成员函数或类的友元函数才能存取类中的私有成员 C.在类中,如果不做特别说明,所有成员的访问权限均为私有的 D.在类中,如果不做特别说明,所有成员的访问权限均为公用的 6、已知X类,则当程序执行到语句X array[3];时,调用了( )次构造函数。 A.0 B.1 C.2 D.3 7、有关析构函数的说法,不正确的是( )。 A.析构函数有且仅有一个 B.析构函数和构造函数一样可以有形参 C.析构函数的功能是在系统释放对象之前作一些内存清理工作 D.析构函数无任何函数类型 8、类定义的内容允许被其对象无限制地存取的是( )。 A.private 部分 B. protected 部分 C.public 部分 D.以上都不对 9、关于常数据成员的说法,不正确的是( )。 A.常数据成员的定义形式与一般常变量的,只不过常数据成员的定义必须出现在类体中 B.常数据成员必须进行初始化,并且不能被更新 C.常数据成员通过构造函数的成员初始化列表进行初始化 D.常数据成员可以在定义时直接初始化 10、运用运算符delete删除一个动态对象时( )。 A.系统首先为该动态对象调用构造函数,再释放其占用的内存 B.系统首先释放该动态对象占用的内存,再为其调用构造函数 C.系统首先为该动态对象调用析构函数,再释放其占用的内存 D.系统首先释放动态对象占用的内存,再为其调用析构函数 11、可在类外用p.a的形式访问派生类对象 p的基类成员a,其中a是( )。 A.私有继承的公用成员 B.公用继承的私有成员 C.公用继承的保护成员 D.公用继承的公用成员 12、在公用继承方式下,有关派生类对象和基类对象的关系,不正确的叙述是(  )。 A.派生类的对象可以赋给基类的对象 B.派生类的对象可以初始化基类的引用 C.派生类的对象可以直接访问基类中的成员 D.派生类的对象的地址可以赋给指向基类的指针 13、设置虚基类的目的是( )。 A.简化程序 B.消除二义性 C.提高运行效率 D.减少目标代码 14、在C++中,用于实现动态多态性的是( )。 A.内联函数 B.重载函数 C.模板函数 D.虚函数 15、不能说明为虚函数的是( )。 A.析构函数 B.构造函数 C.类的成员函数 D.以上都不对 16、如果一个类至少有一个纯虚函数,那么就称该类为( )。 A.抽象类 B.派生类 C.纯基类 D.以上都不对 17、下面关于友元的描述中,错误的是( )。 A.友元函数可以访问该类的私有数据成员 B.一个类的友元类中的成员函数都是这个类的友元函数 C.友元可以提高程序的运行效率 D.类与类之间的友元关系可以继承 18、下列运算符中,( )运算符在C++中不能被重载。 A.&& B.[ ] C.:: D.new 19、模板的使用实际上是将类模板实例化成一个( )。 A.函数 B.对象 C.类 D.抽象类 20、假定MyClass为一个类,则该类的拷贝构造函数的声明语句为( )。 A.MyClass(MyClass x) B.MyClass&(MyClass x) C.MyClass(MyClass &x) D.MyClass(MyClass *x) 二、填空题(前16个空,每空1分,后2个空,每空2分,共20分) 1、类和对象的关系可表述为:类是对象的 ,而对象则是类的 。 2、在C++中,三种继承方式的说明符号为 、 和 ,如果不加说明,则默认的继承方式为 。 3、如果只想保留公共基类的一个复制,就必须使用关键字 把这个公共基类声明为虚基类。 4、若要把void fun( )定义为类A的友元函数,则应在类A的定义中加入语句 。 5、类的静态成员分为 和 。 6、运算符重载要求保持其原来的操作数个数、 、 和语法结构。 7、通过关键字 可以声明模板,通过关键字 指定函数模板的类型参数,有几个类型参数就有几个类型关键字。 8、列出C++中两种用户自定义的数据类型: 、 。 9、构造函数的作用是 。 10、后置自增运算符“++”重载为类的成员函数(设类名为A)的形式为 。 三、阅读下面3个程序,写出程序运行时输出的结果:(共13分) 1、#include <iostream> using namespace std; void fun(int &a,int &b) { int p; p=a; a=b; b=p; } void exchange(int &a,int &b,int &c) { if(a<b) fun(a,b); if(a<c) fun(a,c); if(b<c) fun(b,c); } void main() { int a=12,b=89,c=56; exchange(a,b,c); cout<<"a="<<a<<",b="<<b<< ",c="<<c<<endl; } 2、#include <iostream> using namespace std; class Date { public: Date(int,int,int); Date(int,int); Date(int); Date(); void display(); private: int month, day, year; }; Date::Date(int m, int d, int y) : month(m),day(d),year(y) { } Date::Date(int m,int d):month(m),day(d) { year=2009; } Date::Date(int m) : month(m) { day=1; year=2010; } Date::Date() { month=1; day=1; year=2010; } void Date::display() { cout <<month<<"/"<<day<<"/"<<year<<endl; } void main() { Date d1(12,31,2009); Date d2(12,31); Date d3(1); Date d4; d1.display(); d2.display(); d3.display(); d4.display(); } 3、#include <iostream> using namespace std; class A { public: A() { cout<<"constructing A "<<endl; } ~A() { cout<<"destructing A "<<endl; } }; class B: public A { public: B() { cout<<"constructing B "<<endl; } ~B() { cout<<"destructing B "<<endl; } }; class C : public B { public: C() { cout<<"constructing C "<<endl; } ~C() { cout<<"destructing C "<<endl; } }; void main() { C c1; } 四、编程题(共27分) 1、(10分)已知复数类Complex的声明如下: class Complex { public: Complex(); Complex(double); Complex(double, double); friend Complex operator + (Complex&, Complex&); friend ostream & operator << (ostream&, Complex&); friend istream& operator >> (istream&, Complex&); private: double real, imag; }; 要求: (1)写出该类的所有构造函数的类外定义代码。 (2)写出对运算符“+”、“<<”、“>>”进行重载的运算符重载函数的定义。 2、(17分)下列Base类是一个表示形状的抽象类,area( )为求图形面积的函数,total( )则是一个通用的用以求不同形状的图形面积总和的函数。 class Base { public: virtual double area()=0; }; double total(Base *s[ ], int n) { double sum=0.0; for(int i=0; i<n; i++) sum+=s[i]->area( ); return sum; } 要求: (1)从Base类派生圆类(Circle)、正方形类(Square),圆类新增数据成员半径(radius),正方形类新增数据成员边长(a),圆类和正方形类都有构造函数,修改、显示数据成员值的函数,求面积函数。 (2)写出main( )函数,计算半径为5.5的圆和边长为9.9的正方形的面积和(必须通过调用total函数计算)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值