1. 叙述面向对象编程的特点是什么?(提醒:封装、继承、多态。)
比照面向过程具备形象、封装、继承和多态的特点。
封装是将形象失去的数据和行为相结合,造成了一个有机整体,使得一部分成员充当类与内部的接口,而将其余成员暗藏了起来达到了对成员拜访权限的正当管制,使得不同类之间的影响最小,增强数据安全,简化编程。
继承容许在放弃原有类个性的根底上,进行更具体、更具体的阐明,可能很好反映出非凡概念和个别概念之间的关系,是代码复用的根底机制。
多态使得一段程序可能具备解决多种类型对象的能力,雷同的音讯在不同的对象下会有不同的动作,加强了编程的灵活性。
2. 应用 const 定义常量与用应用 define 定义常量相比,有什么长处?
a. const 常量有数据类型,而宏常量没有数据类型。编译器能够对 const 常量进行类型安全检查,而对宏常量只能字符替换
b. 有些集成化的调试工具能对 const 常量进行调试,对宏常量不能调试
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)
一、定义常量
常量不可批改
const int val = 5;int const val = 5;
与 #define 宏定义常量的区别:
(1)const 常量具备类型,编译器能够进行安全检查;#define 宏定义没有数据类型,只是简略的字符串替换,不能进行安全检查。
(2)有些集成化的调试工具能对 const 常量进行调试,对宏常量不能调试
二、润饰指针
(1)const int* p;// 指针 p 指向的内容是常量,不可扭转。
(2)int* const p; // 指针自身是一个常量,不可扭转。
(3)const int* const p;// 指针自身和指向的内容都是常量,都不能够扭转。
辨别办法,* p 代表对象内容,p 代表指针自身,看 const 润饰的是哪个。
三、在函数中应用 const
润饰函数参数
void function(const int Var);
表明参数在函数体内不能被批改,但此处没有任何意义,Var 自身就是形参,在函数内不会扭转。
包含传入的形参是指针也是一样。
(1)应用援用参数,能够避免创立正本,缩小内存开销,同时能够在函数中对援用参数批改,函数完结后,援用参数的批改依然存在。
(2)如果为了避免对援用参数进行批改,能够对该参数加上 const 关键字。
润饰函数返回值
与润饰一般变量和指针意义差不多,而在传援用时,如果不心愿函数返回值被扭转,就能够增加关键字 const。
四、在类中应用 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. 构造函数的作用是什么?什么时候会被调用?构造函数的执行程序是什么(父类与子类的构造函数、类本身与其数据成员的构造函数)?
构造函数次要用来在创建对象时初始化对象,即为对象成员变量赋初始值。
当类被创立时,主动调用。
执行构造函数的程序:
- 父类的构造函数
- 数据成员的初始化(成员中有类,执行该类的构造函数)
- 子类的构造函数
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;};
(https://common.cnblogs.com/im…]
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)**
函数模板:(https://common.cnblogs.com/images/copycode.gif)]
template<class T> T add(T a, T b)// 函数模板
{return a + b;}
add<int> (10, 17);// 模板实例
complex c1, c2;
add<complex> (c1, c2);
(https://common.cnblogs.com/images/copycode.gif)]
函数模板是 对算法类似,但反对的数据类型不同的一组操作的提炼,以进步程序的重用性。函数模板的实例就是一个用于特定类型的一般函数。通常,编译器可依据实参类型确定模板参数;
add (10, 17); // add<int> (10,17);
complex c1, c2;
add (c1, c2); // add<complex>(c1,c2);
类模板:应用类模板使用户能够为类定义一种模式,使得类中的某些数据成员、某些数据成员函数的参数、返回值和局部变量可能取任意类型(包含零碎预约义和用户自定义)有时候,有两个或多个类,其性能是雷同的,仅仅是数据类型不同,能够应用模板类。[![复制代码](https://common.cnblogs.com/images/copycode.gif)]
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);
};
(https://common.cnblogs.com/images/copycode.gif)]
**51.dynamic-cast 的作用是什么?试举例说明。**
dynamic_cast < Type-id > (expression)
该运算符把 expression 转换为 type-id 类型,并且能够在运行期间检测类型转换是否平安。dynamic_cast 要求转型的目标类型必须是指针或者援用。将基类指针转换为派生类指针,将基类援用转换为派生类援用;转换是有条件的
如果指针(或援用)所指对象的理论类型与转换的目标类型兼容,则转换胜利进行;