C++面试题目_面向对象基础

一个空类有哪些默认的函数?

默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值运算符、取址运算符和 取址运算符const。总共有六个函数。一个示例如下:

Class Empty{
Empty();
Empty(const Empty&);
~Empty();
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
但是,C++默认生成的函数,只有在被需要的时候,才会产生。即当我们定义一个类,而不创建类的对象时,就不会创建类的构造函数、析构函数等。

如果类重载了构造函数,还会有默认的构造函数吗?

不会。

赋值构造函数和拷贝构造函数的区别?

拷贝构造函数是在对象创建时调用的,赋值函数只能被已经存在的对象进行调用。
String a(“hello”);
String b(“world”);
String c=a; //调用拷贝构造函数
c=b; //调用了赋值构造函数
拷贝构造函数和赋值构造函数不同的地方还在于赋值构造函数在复制新值以前需要删除旧值。但是如果自己等于自己的时候,就不能删除原来的值,需要返回this指针。

深拷贝和浅拷贝的区别?

在对象拷贝过程中,如果没有自定义拷贝构造函数,编译器会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量则调用其相应的拷贝构造函数。
浅拷贝存在三个错误:

  1. 原有的内存没释放
  2. 两个指针指向一个内存,互相影响
  3. 一块内存同时释放了两次

重载 前++和后++的区别?

UPInt& UPInt::operator++()     //返回值为引用类型
{  
   *this += 1;  
   return *this;        // 或return ++privateVal;  
}  
//后置 i++ -----------先调用原值,再++  
const UPInt UPInt::operator++(int)    //参数为一个int型,返回值为const对象
{  
   UPInt oldValue = *this;  
   ++(*this);  
   return oldValue;  
}

构造函数和析构函数可以是虚函数吗?

  1. 构造函数不可以为虚函数,因为虚函数表指针就是在构造函数中建立的,如果构造函数为虚函数,无法保证虚函数指针的建立。
  2. 虚析构函数的地址存在于虚函数表中,和普通虚函数别无二致,同时也会像普通的虚函数一样进行覆盖,虽然父子的析构函数名字不一样,但是他们占同一个坑(即父子析构函数在虚函数表中的位置是一样的,否则就不存在多态了)。析构时,到特定的坑中调用该类型的析构函数,其析构函数中又嵌套了很多对父类的析构函数的调用。

c与c++区别,介绍面向对象特性?

  1. C++是面向对象的语言,C语言是面向函数的语言。
  2. C语言不支持函数重载。
  3. C语言中常见的struct和C++常见的class除了访问默认权限不同,别的功能几乎相同。
  4. 封装继承多态

面向对象理解

封装、继承、多态

类的继承、虚继承、虚基类、抽象类?

类的继承/派生类/基类:

  1. 构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。
  2. 如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建。
  3. 子类没有自定义构造函数,系统会给子类默认无参构造函数,并且调用父类的无参构造。子类自定义了构造函数,先检查有没有调用父类的构造函数,没有的话就调用了父类的无参构造。

多继承存在的问题:

  1. 浪费存储空间;
  2. 存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。

虚函数和虚继承的实现原理:

  1. 他们有相似之处,都利用了虚指针(对象)和虚表(类)。
  2. 虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。
  3. 虚函数父类和子类都有虚函数表,而虚继承只有子类有虚继承表。
  4. 类的虚继承表是会被继承到子类的,虚函数不会被继承。但是在多重继承中,派生类会有多个虚函数表。

抽象类:

  1. 包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。
  2. 抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。

类的多态是怎么实现的?

  1. 继承的目的是为了代码复用,而多态的目的是为了实现接口复用。
  2. 类的多态包括 静态多态(函数重载、泛式编程)以及 动态多态(虚函数)。

静态多态,根据传入不同的参数(个数或者类型不同)调用不同的实现。
动态多态,不论传过来的是哪个类的对象,函数都能通过同一接口调用到各自对象的实现的方法。

动态联编:为每一个包含虚函数的建立一个虚函数表,虚函数表的每一个表项存放的是虚函数在内存中的入口地址。
在该类的每个对象中设置一个指向虚函数表的指针,在调用虚函数时,先采用虚指针找到虚函数表,确定虚函数的入口地址在表中的位置,然后获取入口地址完成调用。

虚函数表是共有的吗?
一个类一个独立享有一张虚寒函数表,同一个类的不同对象的虚函数指针指向同一个位置。

虚函数是按照什么顺序来放的?
①虚函数有两个来源,一个是自己定义的,一个是继承父类的。
②所有虚函数排序,继承于父类的虚函数都放前面,新声明函数放在后面。
③确认虚函数的入口地址,继承父类的虚函数,如果被改写了,那么放的就是改写之后的地址,新声明的虚函数的地址就是在当前类的地址。

问题三:多重继承中,派生类有多少张虚函数表?
多重继承了几次,就有多少张虚函数表。

class A{
	virtual fun();
};
class B{
	virtual fun();
};
class C:public B,A{
	virtual fun();
};
int main(int argc, char* argv[])
{
	cout<<sizeof(A)<<endl;
	cout<<sizeof(B)<<endl;
	cout<<sizeof(C)<<endl;
	return 0;
}//4  4  8

public、private、protect继承后属性发生什么变化?

访问范围publicprivateprotected
类里面yesyesyes
实例化的对象yesnono
友元函数yesyesyes
共有继承以后yesyesyes

从基类继承过来的成员在子类中(对子类内的成员)访问权限的变化情况

基类(父类)publicprotectedprivate
公有继承publicprotected不可见
保护继承protectedprotected不可见
私有继承privateprivate不可见
面向对象程序设计》试题 一、单选题(每空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、付费专栏及课程。

余额充值