C++ 面向对象程序设计复习大纲

 这是我在准备C++考试时整理的提纲,若是是经过搜索引擎搜索到这篇博客的师弟师妹,建议仍是先参照PPT和课本,这个大纲也不是很准确,本身总结会更有收获,多去理解含义,不要死记硬背,不然遇到概念辨析题会特别吃亏,若是以为有收获点赞关注,祝考试顺利。ios

 

1.叙述面向对象编程的特色是什么?(提示:封装、继承、多态。)程序员

对比面向过程具备抽象、封装、继承和多态的特色。算法

封装是将抽象获得的数据和行为相结合,造成了一个有机总体,使得一部分红员充当类与外部的接口,而将其余成员隐藏了起来达到了对成员访问权限的合理控制,使得不一样类之间的影响最小,增强数据安全,简化编程。express

继承容许在保持原有类特性的基础上,进行更具体、更详细的说明,可以很好反映出特殊概念和通常概念之间的关系,是代码复用的基础机制。编程

多态使得一段程序可以具备处理多种类型对象的能力,相同的消息在不一样的对象下会有不一样的动做,加强了编程的灵活性。数组

 

2.使用const定义常量与用使用define定义常量相比,有什么优势?安全

a. const常量有数据类型,而宏常量没有数据类型。编译器能够对const常量进行类型安全检查,而对宏常量只能字符替换数据结构

b. 有些集成化的调试工具能对const常量进行调试,对宏常量不能调试 ide

c.const定义的常量在程序运行的过程当中只有一份拷贝,而define定义的常量在内存中有若干拷贝。函数

 

3.用代码说明在标准C++中如何进行输入输出,并解释各语句的含义是什么?

cout<<"hello!"<<"world"; cin>>a>>b;

 

在输入时,从键盘输入的数据先放在键盘缓冲区中,当按回车键时,键盘缓冲区中的数据输入到程序中的输入缓冲区,造成cin流,而后用流提取运算符“>>”从输入缓冲区中提取数据送给程序中的有关变量。

当用cout和流插入运算符“<<”向显示器输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区中的所有数据送到显示器显示出来。

 

4.C++中如何进行静态类型转换,解释并举例说明。

(1)用于类层次结构中基类和派生类之间指针或引用的转换。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,因为没有动态类型检查,因此是不安全的。

class Base { }; class Derived:public Base {}; int main() { Derived D; Base* B = static_cast<Base*> (&D); return 0; }

将派生类型的指针转化为基类型的指针

 

(2)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

 double a; int b=100; a = static_cast<double> (b);

 

(3)把空指针转换成目标类型的空指针(不安全!!)。

(4)把任何类型的表达式转换成void类型。

 

5.阐述C++中函数三种调用的方式实现机制、特色及其实参、形参的格式,最好用代码说明。(提示:传址、传值、引用传递)

在C++中调用函数时有三种参数传递方式:

(1)传值调用;

int main( ) { void swap(int,int); //函数声明 int i=3,j=5; swap(i,j); //调用函数swap return 0; } void swap(int a,int b) //企图经过形参a和b的值互换,实现实参i和j的值互换 { int temp; temp=a; //如下3行用来实现a和b的值互换 a=b; b=temp; }

(2)传址调用(传指针);

用指针类型做为形参的值调用方式,能够经过参数返回修改后的值。

void main( ) { void swap(int *,int *); int i=3,j=5; swap(&i,&j); //实参是变量的地址 } void swap(int *p1,int *p2) //形参是指针变量 { int temp; temp=*p1; //如下3行用来实现i和j的值互换 *p1=*p2; *p2=temp; }

(3)引用传递;

按引用传递,引用实参的引用参数传递给函数,而不是进行参数拷贝。引用类型的形参与相应的实参占用相同的内存空间,改变引用类型形参的值,相应实参的值也会随着变化。

int main( ) { void swap(int &,int &); int i=3,j=5; swap(i,j); return 0; } void swap(int &a,int &b) //形参是引用类型 { int temp; temp=a; a=b; b=temp; }

6.什么是内联函数?为何要使用内联函数?

在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去,这种嵌入到主调函数中的函数成为内联函数。

为了节省参数传递、控制转换等开销,好比:压栈、弹栈、保存现场与恢复现场。

 

7.什么是类的前向声明?使用类的前向声明时,要注意什么?

遇到俩个类相互引用的循环依赖状况

class B; //前向引用声明 class A//A类的定义 { public://外部接口 void f(B b);//以B类对象b为形参的成员函数  }; class B//B类的定义 { public://外部接口 void g(A a);//以A类对象a为形参的成员函数 };

 

前向引用声明,是在引用未定义的类以前,声明该类,使编译器知道那是一个类名。这样,当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义能够在程序的其余地方。

注意:尽管使用了前向引用声明,可是在提供一个完整的类声明以前,不能定义该类的对象,也不能在成员函数中使用该类的对象。只能用于定义指针、引用、以及用于函数形参的指针和引用。当你使用前向引用声明时,你只能使用被声明的符号,而不能涉及类的任何细节。

 

8.什么是先验条件(Precondition),什么是后置条件(Postcondition)?(google)

先演条件是在执行某段代码或正式规范操做以前必须始终为真的条件或谓词。好比输入一个时间必须小于24。

后置条件是在执行某段代码或正式规范操做以后必须始终为真的条件或谓词。好比计算输入数字的平方根程序可能具备结果为数字的后置条件,且其平方等于输入。

 

9.什么是名称空间(namespace)?它的主要做用是什么?要使用一个名称空间中声明的标识符,方式有哪些?

A namespace is a scope.

 C++ provides namespaces to prevent name conflicts

名字空间实质上是一种做用域。名字空间是一种描述逻辑分组的机制,是为了解决C++中的变量、函数命名冲突而服务的。

C++标准程序库中的全部标识符都被定义于一个名为std的namespace中。

因为namespace的概念,使用C++标准程序库的任何标识符时,能够有三种选择:

 

a、直接指定标识符。例如std::ostream而不是ostream。完整语句以下:

#include <iostream> std::cout << "hello!!"<< std::endl;

 

b、使用using关键字进行声明

显然,当某个名字在它本身的名字空间以外频繁使用时,在反复写它时都要加上名字空间来做限定词,是一件使人厌烦的事情。这时,能够经过一个使用声明而清楚掉,只须要在某个地方说明,在这个做用域其后的代码中,使用此名字时能够自动解析出此名字所在的空间。例如:

#include <iostream> using std::cout; using std::endl;

此后在使用cout和endl时,都无需再加上名字空间前缘了:

cout <<"hello!!"<< endl;

 

c、最方便的就是使用指令using namespace std;

一个使用指令能把来自另外一个名字空间的全部名字都变成在当前名字空间内可用,就像这些名字就在当前名字空间中同样。

例如,在一个名字空间内有命令 using namespace std; 则在此后的代码中(当前空间最小局部内),对于全部名字都无需有名字空间前缀便可使用。

#include <iostream> using namespace std;

 

10.什么是重载(Overloading),解释并举例说明?可否根据返回值不一样,对函数进行重载,为何?

C++有两种重载:函数重载和运算符重载。

C++容许用同一函数名定义多个函数,这些函数的参数个数和参数类型不一样。这就是函数的重载(function overloading)。

运算符重载实质上是函数的重载,运算符重载经过运算符函数实现。

int max(int a,int b, int c); double max(double a,double b,double c); long max(long a,long b,long c);

 

不能根据返回值不一样进行重载。

由于调用时不能指定类型信息,编译器不知道你要调用哪一个函数。

例如

float max(int a, int b); int max(int a, int b);

当调用max(1, 2);时没法肯定调用的是哪一个,单从这一点上来讲,仅返回值类型不一样的重载是不该该容许的。

 

11.关键字const的用法有哪些?(google)

 

1、定义常量    

常量不可修改     

const int val = 5int const val = 5;

与#define宏定义常量的区别:

(1)const常量具备类型,编译器能够进行安全检查;#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查。

(2)有些集成化的调试工具能对const常量进行调试,对宏常量不能调试

 

2、修饰指针       

(1)const int* p;         //指针p指向的内容是常量,不可改变。

(2)int* const p;          //指针自己是一个常量,不可改变。

(3)const int* const p;   //指针自己和指向的内容都是常量,都不能够改变。

 

区分方法,*p表明对象内容,p表明指针自己,看const修饰的是哪一个。

 

3、在函数中使用const    

修饰函数参数    

void function(const int Var);  

代表参数在函数体内不能被修改,但此处没有任何意义,Var自己就是形参,在函数内不会改变。

包括传入的形参是指针也是同样。

(1)使用引用参数,能够防止建立副本,减小内存开销,同时能够在函数中对引用参数修改,函数结束后,引用参数的修改仍然存在。

(2)若是为了防止对引用参数进行修改,能够对该参数加上const关键字。

修饰函数返回值           

与修饰普通变量和指针意义差很少,而在传引用时,若是不但愿函数返回值被改变,就能够添加关键字 const 。

 

4、在类中使用const     

修饰类成员变量     

class A { const int nValue; }

 (1)成员常量不可被修改。

(2)只能在初始化列表中被赋值。

修饰类成员函数    

class A { void function()const; }

(1)常成员函数, 它不改变对象的成员变量. 表明只读函数,增长程序的可读性。                

(2)不能调用类中任何非const成员函数。

 

12.操做符new的做用是什么?如何申请单个空间?如何申请动态数组?用new建立一个类的对象时,会发生哪些操做?必要时,请用代码说明。

做用:在堆中申请一段空间,动态分配内存

申请单个空间int *i = new int;        

申请动态数组int *a = new int[10];

new建立类对象须要指针接收,一处初始化,多处使用,做用域是全局,且须要手动释放空间,在堆中动态分配内存,调用构造函数。

 

13.操做符delete的做用是什么?如何删除单个用new申请的空间?如何删除申请的动态数组?用delete删除一个类的对象时,会发生哪些操做?必要时,请用代码说明。

做用:释放所申请的空间

释放单个空间delete i;   

释放动态数组delete []a;

释放在堆中分配的内存,调用析构函数。

 

14.什么是悬挂指针(又称为野指针,Dangling Pointers),其危害是什么?(google)

指针指向非法的内存地址,那么这个指针就是悬挂指针,也叫野指针。意为没法正常使用的指针。野指针形成的危害程度和危害时间未知,由于野指针指向的内存空间,有多是某个重要的数据或其余程序。严重的状况下会形成程序崩溃。

 

15.什么是类?一般一个类中,包含什么样的内容?定义一个类的语法是什么,试举例说明。

类是逻辑上相关的函数与数据的封装,描述了所建立对象共同的属性和方法。类中声明或定义的变量和函数称为成员,类的成员包括数据成员和函数成员,数据成员描述问题的属性,函数成员描述问题的行为。

 

16.什么是对象?什么是类?类与对象的关系是什么?

类是逻辑上相关的函数与数据的封装,它是对问题的抽象描述。

对象是类的某一特定实体。

将整个公司的雇员当作一个类,那么每个雇员就是该类的一个特定实体,也就是一个对象。

类对象的关系:类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于建立对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。

 

17.类中的成员能够用public/protected/private分别进行修饰,这三种成员在什么状况下是能够被访问的?类中没有用public/protected/private修饰的成员,其可访问性是什么,结构体中没有用public/protected/private修饰的成员,其可访问性是什么?

 

public修饰的成员能够在任何地方被访问

private修饰的成员只能由该类中的函数、其友元函数访问;不能被任何其余访问,该类对象也不能访问。

protected修饰的成员能够被该类中函数、子类函数、友元函数访问;但不能被该类对象访问。

public能够被访问,没有修饰,类的默认为private,struct默认为public。

 

18.什么是封装?其做用是什么?(google)

封装就是将抽象获得的数据和行为(或功能)相结合,造成一个有机的总体,也就是将数据与操做数据的函数代码进行有机结合,造成类。

做用:

使一部分红员充当类与外部的接口,而将其余成员隐藏起来,这样就达到了对成员访问权限的合理控制,使不一样类之间的相互影响减小到最低限度,进而保护数据加强数据的安全性和简化程序编写工做。

 

19.什么是构造函数?构造函数有返回值吗?构造函数如何命名?构造函数能够重载吗?什么是缺省构造函数(default constructor)?什么状况下,类中会有缺省构造函数?

构造函数主要用来在建立对象时初始化对象, 即为对象成员变量赋初始值。

构造函数没有返回值。

构造函数是一个与其所在的类同名的函数。

构造函数能够重载。可是, 每一个构造函数必须有不一样的函数签名。

若是构造函数没有参数,或者构造函数的全部参数都有默认值,就能够称其为缺省构造函数。一个类中,只能有一个缺省构造函数。

当没有定义构造函数或者定义的构造函数没有参数时,类中会有缺省构造函数。

 

20.若父类中没有缺省构造函数,则对派生类的构造函数有什么要求?

若是父类是一个完好省参数的构造函数,那么对于派生类一旦没有构造函数,那么就不会自动的先构造父类的构造函数,这是不容许的。

派生类中必定要有构造函数。

BaseballTeam(const string s[], int si) : Team(si)

派生类的构造函数经过初始化列表,对基类进行初始化。

 

21.构造函数的做用是什么?何时会被调用?构造函数的执行顺序是什么(父类与子类的构造函数、类自身与其数据成员的构造函数)?

构造函数主要用来在建立对象时初始化对象, 即为对象成员变量赋初始值。

当类被建立时,自动调用。

执行构造函数的顺序:

1. 父类的构造函数

2. 数据成员的初始化(成员中有类,执行该类的构造函数)

2. 子类的构造函数

 

22.什么是类做用域(Class scope)、文件做用域(file scope)、函数做用域(function scope)?

类做用域:

类是有名成员的集合,类X的成员m具备类做用域,对成员m的访问方式有以下三种:

1)若是X的成员函数中没有声明同名的局部做用域标识符,那么能够直接使用成员m

2)经过表达式x.m或X::m(访问静态成员)

3)经过ptr->m,其中ptr为指向X类的一个对象的指针

文件做用域:

在函数外部声明的变量只在当前文件范围内(包括文件内全部定义的函数)可用

在其余文件不可用。要使变量具备文件做用域,必须在变量的声明前加static关键字。

当多个源文件连接成一个程序时,static能够避免一个文件中的全局变量与其它文件中的变量同名而发生冲突。

函数做用域:

(1)指在函数定义或者复合语句中,从标识符的定义点开始到函数或者一对花括号之间的程序段。

(2)在同一个局部做用域内不能出现相同名字的两个局部变量(包括形参)。

(3)一个函数内的复合语句又是一个局部做用域,也就是在函数内有某个变量时,复合语句中能够有另一个同名字的变量。

 

23.为何拷贝构造函数(copy constructor)的参数必须是按引用传递(by reference)而不能是按值传递(by value)?

1.无限递归调用:

当一个对象须要以值方式传递时编译器会生成代码调用它的拷贝构造函数以生成一个复本。若是类A的拷贝构造函数是以值方式传递一个类A对象做为参数的话,当须要调用类A的拷贝构造函数时,须要以值方式传进一个A的对象做为实参;而以值方式传递须要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数致使又一次调用类A的拷贝构造函数,这就是一个无限递归。

2在某些情况下,类内成员变量须要动态开辟堆内存,若是实行位拷贝,也就是把对象里的值彻底复制给另外一个对象,如A=B。这时,若是B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

 

24.拷贝构造函数(复制构造函数)的做用是什么?什么是浅拷贝?什么是深拷贝?(google)

复制构造函数由编译器调用来完成一些基于同一类的其余对象的构件及初始化。

浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,并且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不一样地址的指针。

 

25.全局对象(Global scope objects)的构造函数、析构函数分别是何时被调用的?

   自动局部对象(Automatic local objects)的构造函数、析构函数分别是何时被调用的?

   静态局部对象(static local objects)的构造函数、析构函数分别是何时被调用的?

a.全局变量构造函数程序运行前被调用,在main()函数返回后才被中对象才被销毁,析构函数在程序结束前最后被调用。

b.自动局部变量,当程序执行到对象定义时,调用自动局部对象的构造函数。该对象的析构函数在对象离开范围时调用(即离开定义对象的块时)。自动对象的构造函数与析构函数在每次对象进人和离开范围时调用。

c.静态局部对象的构造函数只在程序执行首次到达对象定义时调用一次,对应的析构函数在main终止或调用exit函数时调用。

 

26.什么是初始化列表(Initialization Sections)?它的做用是什么?(提示:通常数据成员的初始化、常成员的初始化,对象成员构选函数的选择、父类构造函数的选等)。

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每一个数据成员后面跟一个放在括号中的初始化式。

class A { public: int a; float b; A(): a(0),b(9.9) {} //构造函数初始化列表 };

初始化列表做用:通常数据成员的初始化、常成员的初始化,对象成员构选函数的选择、父类构造函数的选择。

 

27.什么是纯虚函数?什么是抽象数据类型(ADT)?抽象类的做用是什么?抽象类是否可实例化?抽象类的什么样子类能够实例化?(google)

纯虚函数是没有函数体的虚函数,它的实现留给该基类的派生类去作,这就是纯虚函数的做用。

抽象类是一种特殊的类,它是为了抽象和设计的目的而创建的,它处于继承层次结构的较上层。

抽象类不可实例化,只能够派生。

抽象类派生的子类必须重置基类的纯虚函数才能实现实例化。

 

抽象数据类型是具备相似行为的特定类型的数据结构的数学模型:或者具备相似语义的一种或者多种程序设计语言的数据类型。

 

抽象数据类型的描述包括给出抽象数据类型的名称、数据的集合、数据之间的关系和操做的集合等方面的描述。抽象数据类型的设计者根据这些描述给出操做的具体实现,抽象数据类型的使用者依据这些描述使用抽象数据类型。

抽象数据类型描述的通常形式以下:

ADT 抽象数据类型名称

{

数据对象:

……

数据关系:

……

操做集合:

操做名1:

……

……

操做名n:

}ADT抽象数据类型名称

抽象数据类型定义(ADT)

做用:抽象数据类型可使咱们更容易描述现实世界。例:用线性表描述学生成绩表,用树或图描述遗传关系。

定义:一个数学模型以及定义在该模型上的一组操做。

关键:使用它的人能够只关心它的逻辑特征,不须要了解它的存储方式。定义它的人一样没必要要关心它如何存储。

例:线性表这样的抽象数据类型,其数学模型是:数据元素的集合,该集合内的元素有这样的关系:除第一个和最后一个外,每一个元素有惟一的前趋和惟一的后继。能够有这样一些操做:插入一个元素、删除一个元素等。

 

 

28.什么是this指针,其做用是什么?

this指针是一个隐含于每个成员函数中的特殊指针。它是一个指向正在被该成员函数操做的对象,也就是要操做该成员函数的对象。经过this指针能够访问当前对象的全部成员。

this做用域是在类内部,当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,编译器会自动将对象自己的地址做为一个隐含参数传递给函数。

在如下场景中,常常须要显式引用this指针:

(1)在类的非静态成员函数中返回类对象自己的时候,直接使用 return *this,例如:实现对象的链式引用。

(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。

(3)避免对同一对象进行赋值操做。

if(&pointer!=this) //同一对象之间的赋值没有意义,因此要保证pointer不等于this { X=pointer.X; Y=pointer.Y; }

 

29.什么是友元(friend)函数?为何要使用友员函数?

友元函数是在类声明中由关键字friend修饰说明的非成员函数或其它类的成员函数,在它的函数体中可以经过对象名访问 private 和 protected成员

友元函数能够访问这个类中的私有成员,增长灵活性,使程序员能够在封装和快速性方面作合理选择。

友元是C++提供的一种对数据封装和数据隐藏的破坏机制。

 

 

30.如何防止一个头文件被多重包含?举例说明。

#include "a.h"

#include "b.h"

若是a.h和b.h都包含了一个头文件x.h。那么x.h在此也一样被包含了两次,只不过它的形式不是那么明显而已。

可使用条件编译。

#ifndef _HEADERNAME_H #define _HEADERNAME_H ..//(头文件内容) #endif

当头文件第一次被包含时,它被正常处理,符号_HEADERNAME_H被定义为1。若是头文件被再次包含,经过条件编译,它的内容被忽略。

符号_HEADERNAME_H按照被包含头文件的文件名进行取名,以免因为其余头文件使用相同的符号而引发的冲突。可是,你必须记住预处理器仍将整个头文件读入,即便这个头文件全部内容将被忽略。因为这种处理将托慢编译速度,因此若是可能,应该避免出现多重包含。

 

31.什么是运算符重载?为何要使用运算符重载?如何进行运算符重载,举例说明。

运算符重载,就是对已有的运算符从新进行定义,赋予其另外一种功能,以适应不一样的数据类型。

扩展C++中提供的运算符的适用范围,以用于类所表示的抽象数据类型。同一个运算符,对不一样类型的操做数,所发生的行为不一样。

运算符重载的函数通常地采用以下两种形式:成员函数形式和友元函数形式。这两种形式均可访问类中的私有成员。

class Complex { public: Complex( ) { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } Complex operator+( const Complex & ) const; //重载为成员函数 friend Complex operator+(Complex &c1,Complex &c2); //重载为友员函数 void display( ); private: double real; double imag; };

 

32.为何重载为全局函数的运算符一般要比重载为成员函数的运算符多一个参数?举例说明。

当重载为成员函数时,会有一个this指针,指向当前的类,因此只须要一个参数就能够了。

而当重载为全局函数时,将没有隐含的参数this指针,这样将会多一个参数。

Complex operator +(Complex&); friend Complex operator+(Complex &c1,Complex &c2);

 

33.什么是析构函数?析构函数有返回值吗?析构函数如何命名?析构函数能够重载吗?

与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。

析构函数没有返回值。

名字与类名相同,在前面加‘~’。

析构函数不返回任何值,没有函数类型,也没有函数参数,所以它不能被重载。

 

34.析构函数的做用是什么?何时会被调用?为何析构函数一般是虚函数,若是不是虚函数,会如何?(google)

析构函数对象消亡时即自动被调用。

做用:清空并释放对象先前建立或者占用的内存资源。

若是析构函数不被声明成虚函数,则编译器采用的绑定方式是静态绑定,在删除基类指针时,只会调用基类析构函数,而不调用派生类析构函数,这样就会致使基类指针指向的派生类对象析构不彻底。

如果将析构函数声明为虚函数,无论派生类的析构函数前是否加virtual(能够理解为编译器优化),都构成重写。基类的指针指向派生类的对象,而后调用重写虚函数——析构函数,构成了多态,而多态与类型无关,只与对象有关,因此就可以调用的就是派生类的析构函数了。

 

35.在一个类中,为何静态成员函数(static member function)中不能使用this指针?

静态成员函数并非针对某个类的实例对象,而是属于整个类的,为全部的对象实例所共有。他在做用域的范围内是全局的,独立于类的对象以外的。他只对类内部的静态成员变量作操做。当实例化一个类的对象时候,里面不存在静态成员的。this指针是至关于一个类的实例的指针,this是用来操做对象实例的内容的,既然静态成员函数和变量都是独立于类的实例对象以外的,他就不能用this指针。也不能操做非静态成员。

 

36.若是要编写一段程序,跟踪类A所建立的实例的个数,请叙述编写程序的大致思路。

#include<iostream> #include<string.h> #include<stdio.h> using namespace std; class A { public: A() { i++; } ~A() { i--; } int get() { return i; } private: static int i; }; int A::i(0); int main() { A c; A b; A e; cout<<c.get()<<endl; A *p=new A; cout<<c.get()<<endl; delete p; cout<<c.get()<<endl; return 0; }

 

37.什么是C++中的三大函数(The Big Three)?(google)

Big Three: 是指 若是一个类要实现下面某一个成员函数,通常都要一块儿实现另外两个:

   1)Desconstructor       析构函数

   2)  copy constructor    拷贝构造函数

   3)  operator =          赋值函数

 

38.什么是UML?如何画UML中的类图?什么是类与类之间依赖关系、关联关系、包含关系?试举例说明这三种类之间的关系。这三种关系如何和UML图表示?

UML统一建模语言,UML语言是一种可视化的的面向对象建模语言,描述软件模型的静态结构、动态行为及模块组织与管理。

依赖关系:一个事物的变化可能会影响到使用它的另外一个事物。举例:驾驶员(源)开车(目标)。

关联关系:一个类的对象和另外一个类的对象之间相互做用。举例:老师和学生,小明的语文老师是张老师,张老师的学生有小明。

包含关系:汇集和组合

汇集表示类之间的关系是总体与部分的关系。举例:班级成员和学生。

组合是指总体拥有各个部分,总体和部分共存,总体不存在了,部分也会随之消失。举例:打开一个视窗口,它由标题、外框和显示区域组成,视窗口是一个总体,它消失了,部分也就随之消失了。

继承关系:在UML中称为泛化。举例:鸭子和鸟,鸭子是一种鸟,继承了鸟的特性。

 

39.常见的类与类之间的关系有哪几种,举例说明每种关系的对应UML图如何画?两个什么样的类能够实现为继承关系?(google)

依赖关系、关联关系、包含关系、继承关系。

具备共同属性的两个类能够实现继承关系。

 

40.父类成员中的public、protected、private成员,哪些在子类中是能够访问的?

在公有继承、私有继承、受保护继承三种继承方式下,父类成员中的public、protected、private成员被继承到子类后,其可访问性分别是什么?

派生类是否能够继承父类的构造函数和析构函数?

public 和protected是能够访问的,private不可访问。

公有继承:public、protected、private

私有继承:private、private、private

保护继承:protected、protected、private

派生类不能继承父类的构造函数和析构函数。

 

41.多重继承会带来什么问题?在C++中是如何解决的?

问题1:类DC的对象中存在多个同名成员 x, 应如何使用?

问题2:类DC的对象中,存在两份来自类BC0的成员K,如何区分?

解决方案:

在BC1类和BC2类继承BC0时,其前面加上virtual关键字就能够实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会先自动加一个BC0的拷贝,当再次请求一个BC0的拷贝时就会被忽略,以保证继承类成员函数的惟一性。

class BC0 { public: int K; }; class BC1 : virtual public BC0 { public: int x; }; class BC2 : virtual public BC0 { public: int x; }; class DC : public BC1, public BC2 { }; void main( ) { DC d; //虚继承使得BC0仅被DC间接继承一份 d.K = 13; // OK }

 

42.对于函数调用,什么是前期绑定(Early Binding,又称为静态联编)?什么是的后期绑定(Late Binding,又称为动态联编)?重载函数是后期绑定吗,若是不是为何?

绑定:程序自身彼此关联的过程,肯定程序中的操做调用与执行该操做的代码间的关系。例如把一个标示符名和一个存储地址联系在一块儿的过程。

用面向对象的术语讲,就是把一条消息和一个对象的方法相结合的过程。

按照绑定进行的阶段的不一样,能够分为静态绑定和动态绑定两种。

静态绑定:绑定工做在编译链接阶段完成。

由于绑定过程是在程序开始执行以前进行的,所以也称为早期绑定或前绑定。

在编译、链接过程当中,系统就能够根据类型匹配等特征肯定程序中操做调用与执行该操做代码的关系,即肯定了某一个同名标识究竟是要调用哪一段程序代码。

动态绑定:和静态绑定相对应,绑定工做在程序运行阶段完成的。

class A { public: virtual void Get( ); }; class B : public A { public: virtual void Get( ); }; void MyFunction( A * pa ) { pa->Get( ); }

pa->Get( ) 调用的是 A::Get( )仍是B::Get( ),编译时没法肯定,由于不知道MyFunction被调用时,形参会对应于一个 A 对象仍是B对象。

因此只能等程序运行到 pa->Get( )了,才能决定到底调用哪一个Get( )。

重载函数是静态绑定。

 

43.要让一个函数调用表现出多态特征,必须知足哪些条件?

a.必须存在继承关系;

b.子类重写父类的方法。继承关系中必须有同名的虚函数,而且它们是覆盖关系(重载不行)。

c.存在基类的指针,经过该指针调用虚函数。

 

44.简述虚函数动态绑定的实现原理。

构造函数中为对象的虚指针赋值,经过多态类型的指针或引用调用成员函数时,经过虚指针找到虚表,进而找到所调用的虚函数的入口地址,经过该入口地址调用虚函数。

 

45.什么是隐藏(hiding)、覆盖(overriding)、重载(overloading)?对比它们的异同?以C++代码为例进行说明。

若基类 B 定义了非虚方法 m,同时其派生类 D 也定义了方法m,此时,咱们说派生类方法 D::m 隐藏了继承自基类的同名方法 B::m 。因为函数签名不一样,因此两者不构成重置。故D::m隐藏了B::m。

class B { public: void m(int x) { … } }; class D : public B { public: void m ( )//因为函数签名不一样,因此两者不构成重置。  { … } }; int main( ) { D d1 ; d1.m(); // invokes D::m() d1.m(10); // ERROR d1.B::m(10); // OK return 0; }

 

覆盖(override)是指派生类中存在从新定义的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体不一样,当派生类对象调用派生类中该同名函数时会自动调用派生类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫作覆盖。

class B { public: virtual void m( ) { … } }; class D : public B { public: void m ()//重置了基类方法,仍然为虚函数  { } }; int main( ) { B*p= new D; p -> m(); // 动态绑定 D::m() return 0; }

 

重载:若是顶层函数有不一样的签名,则函数名能够相同。

class B { public: virtual void m( ) { … } }; class D : public B { public: void m ()//重置了基类方法,仍然为虚函数  { } }; int main( ) { B*p= new D; p -> m(); // 动态绑定 D::m() return 0; }

若是同一类中的函数有不一样的签名,则函数名能够相同。

class C { public: C( ) { … } // default constructor C( int x ) { … } // convert constructor  }; void print( double d ); void print( char * ); int main( ) { C c1,c2(26); print(100.123); print( "100.123" ); }

 

编译过程当中绑定函数调用和对应的函数体

 

46.什么是多态?

一个组合的希腊词。含义:一种物质有多种形态。在专业术语中,多态是一种运行时绑定机制(run-time binding) ,经过这种机制,实现将函数名绑定到函数具体实现代码的目的。

 

47.什么是切片(Slicing)?(注意参考讲义)

派生类的存储结构与基类的存储结构存在着“粘接(splice)”关系:

当子类对象拷贝到父类对象时,父类对象中只存在父类定义的成员,而不会出现任何子类中的成员。

 

48.使用异常处理机制的好处是什么?

1.将常规代码与错误处理代码的分离

2.实如今调用栈中传播异常

3.实现对不一样的错误类型进行分类

 

49.简述C++中的异常处理机制。要捕获某段代码中的全部异常,应该如何编写代码?

C++ 用try和catch进行异常处理,当try块出现异常,则catch中匹配相应的异常处理,若catch块中没有匹配该异常对象的语句,则转向外一层的try、catch语句,若一直退回到主函数都没法处理异常,则由系统调用terminate()函数终止程序。用异常规格(exception specification)列出函数可能会抛出全部异常的类型。

 

50.分别举例说明用于算法抽象的函数模板和用于数据抽象的类模板。(google)

函数模板:

template<class T> T add( T a, T b )//函数模板 { return a + b; } add<int> ( 10, 17 );//模板实例 complex c1, c2; add<complex> ( c1, c2 );

函数模板是  对算法类似,但支持的数据类型不一样的一组操做的提炼,以提升程序的重用性。

函数模板的实例就是一个用于特定类型的普通函数。

一般,编译器可根据实参类型肯定模板参数;

add ( 10, 17 ); // add<int> (10,17); complex c1, c2; add ( c1, c2 ); // add<complex>(c1,c2);

类模板:

使用类模板使用户能够为类定义一种模式,使得类中的某些数据成员、某些数据成员函数的参数、返回值和局部变量可以取任意类型(包括系统预约义和用户自定义)

有时候,有两个或多个类,其功能是相同的,仅仅是数据类型不一样,可使用模板类。

template<class T>//声明模板 class Array { T* array; int size; public: Array( int ); T& operator[ ]( int ); }; class charArray { char *array; int size; public: charArray( int ); char& operator[](int); }; class intArray { int *array; int size; public: intArray( int ); int& operator[](int); };

 

51.dynamic-cast的做用是什么?试举例说明。

 dynamic_cast < Type-id > ( expression )

该运算符把expression转换为type-id类型,而且能够在运行期间检测类型转换是否安全。dynamic_cast要求转型的目的类型必须是指针或者引用。

将基类指针转换为派生类指针,将基类引用转换为派生类引用;

转换是有条件的

若是指针(或引用)所指对象的实际类型与转换的目的类型兼容,则转换成功进行;

不然如执行的是指针类型的转换,则获得空指针;如执行的是引用类型的转换,则抛出异常。

相关文章
相关标签/搜索