不熟悉C++面向对象?了解C++面向对象编程,看这篇够了!
一、对比面向过程具有抽象、封装、继承和多态的特点:
封装是将抽象得到的数据和行为相结合,形成了一个有机整体,使得一部分成员充当类与外部的接口,而将其他成员隐藏了起来达到了对成员访问权限的合理控制,使得不同类之间的影响最小,加强数据安全,简化编程。
继承允许在保持原有类特性的基础上,进行更具体、更详细的说明,能够很好反映出特殊概念和一般概念之间的关系,是代码复用的基础机制。
多态使得一段程序能够具有处理多种类型对象的能力,相同的消息在不同的对象下会有不同的动作,增强了编程的灵活性。
![](https://upload-images.jianshu.io/upload_images/24563956-c3b156937d1f0bc2.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
二、使用const定义常量与用使用define定义常量相比,有什么优点?
a. const常量有数据类型,而宏常量没有数据类型。编译器可以对const常量进行类型安全检查,而对宏常量只能字符替换
b. 有些集成化的调试工具能对const常量进行调试,对宏常量不能调试
c.const定义的常量在程序运行的过程中只有一份拷贝,而define定义的常量在内存中有若干拷贝。
三、用代码说明在标准C++中如何进行输入输出,并解释各语句的含义是什么?
cout<<"hello!"<<"world";
cin>>a>>b;
在输入时,从键盘输入的数据先放在键盘缓冲区中,当按回车键时,键盘缓冲区中的数据输入到程序中的输入缓冲区,形成cin流,然后用流提取运算符“>>”从输入缓冲区中提取数据送给程序中的有关变量。
当用cout和流插入运算符“<<”向显示器输出数据时,先将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区中的全部数据送到显示器显示出来。
![](https://upload-images.jianshu.io/upload_images/24563956-869e92a0f358eaf9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
四、阐述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;
}
![](https://upload-images.jianshu.io/upload_images/24563956-c480ea3d780ca02c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
五、什么是类的前向声明?使用类的前向声明时,要注意什么?
遇到俩个类相互引用的循环依赖情况
class B; //前向引用声明
class A//A类的定义
{ public://外部接口
void f(B b);//以B类对象b为形参的成员函数
}; class B//B类的定义
{ public://外部接口
void g(A a);//以A类对象a为形参的成员函数
};
前向引用声明,是在引用未定义的类之前,声明该类,使编译器知道那是一个类名。这样,当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义可以在程序的其他地方。
注意:尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能定义该类的对象,也不能在成员函数中使用该类的对象。只能用于定义指针、引用、以及用于函数形参的指针和引用。当你使用前向引用声明时,你只能使用被声明的符号,而不能涉及类的任何细节。
六、什么是内联函数?为什么要使用内联函数?
在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去,这种嵌入到主调函数中的函数成为内联函数。为了节省参数传递、控制转换等开销,比如:压栈、弹栈、保存现场与恢复现场。
操作符new的作用是什么?如何申请单个空间?如何申请动态数组?用new创建一个类的对象时,会发生哪些操作?必要时,请用代码说明。
作用:在堆中申请一段空间,动态分配内存
申请单个空间int *i = new int;
申请动态数组int *a = new int[10];
new创建类对象需要指针接收,一处初始化,多处使用,作用域是全局,且需要手动释放空间,在堆中动态分配内存,调用构造函数。
![](https://upload-images.jianshu.io/upload_images/24563956-2a4f5ec727a3b9fc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
七、操作符delete的作用是什么?如何删除单个用new申请的空间?如何删除申请的动态数组?用delete删除一个类的对象时,会发生哪些操作?必要时,请用代码说明。
作用:释放所申请的空间
释放单个空间delete i;
释放动态数组delete []a;
释放在堆中分配的内存,调用析构函数。
八、什么是对象?什么是类?类与对象的关系是什么?
类是逻辑上相关的函数与数据的封装,它是对问题的抽象描述。
对象是类的某一特定实体。
将整个公司的雇员看成一个类,那么每一个雇员就是该类的一个特定实体,也就是一个对象。
类对象的关系:类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
九、类中的成员可以用public/protected/private分别进行修饰,这三种成员在什么情况下是可以被访问的?类中没有用public/protected/private修饰的成员,其可访问性是什么,结构体中没有用public/protected/private修饰的成员,其可访问性是什么?
public修饰的成员可以在任何地方被访问
private修饰的成员只能由该类中的函数、其友元函数访问;不能被任何其他访问,该类对象也不能访问。
protected修饰的成员可以被该类中函数、子类函数、友元函数访问;但不能被该类对象访问。
public可以被访问,没有修饰,类的默认为private,struct默认为public。
![](https://upload-images.jianshu.io/upload_images/24563956-b42047bf34550145.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
十、什么是封装?其作用是什么?(google)
封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的函数代码进行有机结合,形成类。
作用:
使一部分成员充当类与外部的接口,而将其他成员隐藏起来,这样就达到了对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而保护数据增强数据的安全性和简化程序编写工作。
十一、什么是构造函数?构造函数有返回值吗?构造函数如何命名?构造函数可以重载吗?什么是缺省构造函数(default constructor)?什么情况下,类中会有缺省构造函数?
构造函数主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。
构造函数没有返回值。
构造函数是一个与其所在的类同名的函数。
构造函数可以重载。但是, 每个构造函数必须有不同的函数签名。
如果构造函数没有参数,或者构造函数的所有参数都有默认值,就可以称其为缺省构造函数。一个类中,只能有一个缺省构造函数。
当没有定义构造函数或者定义的构造函数没有参数时,类中会有缺省构造函数。
![](https://upload-images.jianshu.io/upload_images/24563956-af2be6a2061e88b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
十二、若父类中没有缺省构造函数,则对派生类的构造函数有什么要求?
如果父类是一个无缺省参数的构造函数,那么对于派生类一旦没有构造函数,那么就不会自动的先构造父类的构造函数,这是不允许的。
派生类中一定要有构造函数。
BaseballTeam(const string s[], int si) : Team(si)
派生类的构造函数通过初始化列表,对基类进行初始化。
十三、构造函数的作用是什么?什么时候会被调用?构造函数的执行顺序是什么(父类与子类的构造函数、类自身与其数据成员的构造函数)?
构造函数主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。
当类被创建时,自动调用。
执行构造函数的顺序:
父类的构造函数
数据成员的初始化(成员中有类,执行该类的构造函数)
子类的构造函数
![](https://upload-images.jianshu.io/upload_images/24563956-38a24ef7c4d043db.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
十四、什么是类作用域(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)一个函数内的复合语句又是一个局部作用域,也就是在函数内有某个变量时,复合语句中可以有另外一个同名字的变量。
十五、为什么拷贝构造函数(copy constructor)的参数必须是按引用传递(by reference)而不能是按值传递(by value)?
1.无限递归调用:
当一个对象需要以值方式传递时编译器会生成代码调用它的拷贝构造函数以生成一个复本。如果类A的拷贝构造函数是以值方式传递一个类A对象作为参数的话,当需要调用类A的拷贝构造函数时,需要以值方式传进一个A的对象作为实参;而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导致又一次调用类A的拷贝构造函数,这就是一个无限递归。
2在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
![](https://upload-images.jianshu.io/upload_images/24563956-7dd605848b452ca3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
十六、拷贝构造函数(复制构造函数)的作用是什么?什么是浅拷贝?什么是深拷贝?(google)
复制构造函数由编译器调用来完成一些基于同一类的其他对象的构件及初始化。
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
十七、全局对象(Global scope objects)的构造函数、析构函数分别是什么时候被调用的?
自动局部对象(Automatic local objects)的构造函数、析构函数分别是什么时候被调用的?
静态局部对象(static local objects)的构造函数、析构函数分别是什么时候被调用的?
a.全局变量构造函数程序运行前被调用,在main()函数返回后才被中对象才被销毁,析构函数在程序结束前最后被调用。
b.自动局部变量,当程序执行到对象定义时,调用自动局部对象的构造函数。该对象的析构函数在对象离开范围时调用(即离开定义对象的块时)。自动对象的构造函数与析构函数在每次对象进人和离开范围时调用。
c.静态局部对象的构造函数只在程序执行首次到达对象定义时调用一次,对应的析构函数在main终止或调用exit函数时调用。
![](https://upload-images.jianshu.io/upload_images/24563956-ddf6db8541fe5cc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
十八、父类成员中的public、protected、private成员,哪些在子类中是可以访问的?
在公有继承、私有继承、受保护继承三种继承方式下,父类成员中的public、protected、private成员被继承到子类后,其可访问性分别是什么?
派生类是否可以继承父类的构造函数和析构函数?
public 和protected是可以访问的,private不可访问。
公有继承:public、protected、private
私有继承:private、private、private
保护继承:protected、protected、private
派生类不能继承父类的构造函数和析构函数。
十九、多重继承会带来什么问题?在C++中是如何解决的?
问题1:类DC的对象中存在多个同名成员 x, 应如何使用?
问题2:类DC的对象中,存在两份来自类BC0的成员K,如何区分?
解决方案:
在BC1类和BC2类继承BC0时,其前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会先自动加一个BC0的拷贝,当再次请求一个BC0的拷贝时就会被忽略,以保证继承类成员函数的唯一性。
![](https://upload-images.jianshu.io/upload_images/24563956-efc8c53d212f4cc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
二十、要让一个函数调用表现出多态特征,必须满足哪些条件?
a.必须存在继承关系;
b.子类重写父类的方法。继承关系中必须有同名的虚函数,并且它们是覆盖关系(重载不行)。
c.存在基类的指针,通过该指针调用虚函数。
![](https://upload-images.jianshu.io/upload_images/24563956-2d68ba6e789d3e47.gif?imageMogr2/auto-orient/strip)
如果你喜欢这篇文章的话,动动小指,加个关注哦~
如果你也想成为程序员,想要快速掌握编程,这里为你分享一个学习企鹅圈子!
里面有资深专业软件开发工程师,在线解答你的所有疑惑~C++入门“so easy”
编程学习书籍:
![](https://upload-images.jianshu.io/upload_images/24563956-178bd2e18b5d99bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
编程学习视频:
![](https://upload-images.jianshu.io/upload_images/24563956-884a3bf8b90c7e6b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)