某公司部分面试题
1.字符串处理 string sContent=“Af23HJHJ3244k”;
请将字符串中的数字转换为*;
for(int i=0;i<sContent.length();i++)
{
if('0'<= sContent[i] && '9' >= sContent[i])
{
sContent[i]='*';
}
}
cout<< sContent<<endl;
2.举例说明回调函数的用法
什么是回调函数?
回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。
回调函数是通过函数指针来实现。具体的示例示例如下:
#include "stdafx.h"
#include <iostream>
using namespace std;
typedef int(*pFun)(int); //定义一个函数指针类型
//函数功能:回调函数测试函数
//参数: pFun pCallback[IN] -- 函数指针,用于指针回调函数
//返回值: 无
void Caller(pFun pCallback)
{
cout << "准备执行回调函数..." << endl;
int ret = pCallback(1);
cout << "函数处理结果:" << ret << endl;
}
//函数功能:真正的回调函数
//参数: int iParam[IN] -- 输入参数
//返回值: int -- 执行结果
int realCallbackFun(int iParam)
{
cout << "进入回调函数..." << endl;
return iParam + 1;
}
int main(int argc, char* argv[])
{
Caller(realCallbackFun);
getchar();
return 0;
}
3. 解释虚析构函数的作用
防止内存泄露,定义一个基类的指针p,在delete p时,如果基类的析构函数是虚函数,这时只会看p所赋值的对象,如果p赋值的对象是派生类的对象,就会调用派生类的析构函数(毫无疑问,在这之前也会先调用基类的构造函数,在调用派生类的构造函数,然后调用派生类的析构函数,基类的析构函数,所谓先构造的后释放);如果p赋值的对象是基类的对象,就会调用基类的析构函数,这样就不会造成内存泄露。
如果基类的析构函数不是虚函数,在delete p时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露。
4.解释什么是函数重载
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为zh重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
5.解释或举例说明操作符重载的意义
运算符重载是为了解决类对象之间的运算的,通常的运算符只用于算术运算,如常量int之间,因为编译器已经定义了;而一个类两个对象之间成员进行运算必须重新定义,让编译器在遇到对象运算时能按我们要求的进行运算,这就是运算符重载的意义,即重定义运算符,因此你可以看到,运算符重载就是为类对象服务的,那么两个对象的成员进行运算那必须先获得对象本身啦,所以运算符重载参数必须含有类指针或引用,这是主要客户。
运算符重载的声明operator 关键字告诉 编译器,它是一个运算符重载,后面是相关运算符的符号,如+。返回类型是在使用这个运算符时获得的类型。对于这个+运算符重载,返回类型与包含类一样,但这种情况并不是必需的。两个参数就是要操作的对象。对于二元运算符(带两个参数),如+和-运算符,第一个参数是放在运算符左边的值,第二个参数是放在运算符右边的值。
实例:
1.重载为类的成员函数
#include<iostream.h>
class complex
{
public:
complex()
{
real = image = 0;
}
complex(double r, double i)
{
real = r; image = i;
}
complex operator+(const complex&c);
complex operator-(const complex&c);
complex operator*(const complex&c);
complex operator/(const complex&c);
friend void printf(const complex&c);
private:
double real, image;
};
inline complex complex::operator+(const complex& c)
{
return complex(real + c.real, image + c.image);
}
inline complex complex::operator-(const complex&c)
{
return complex(real - c.real, image - c.image);
}
inline complex complex::operator*(const complex&c)
{
return complex(real*c.real - image*c.image, real*c.image + image*c.real);
}
inline complex complex::operator/(const complex&c)
{
return complex((real*c.real + image*c.image) / (c.real*c.real+c.image*c.image),
(image*c.real - real*c.image) / (c.real*c.real + c.image*c.image));
}
2.重载为类的友员函数
#include<iostream.h>
class complex
{
public:
complex()
{
real = image = 0;
}
complex(double r, double i)
{
real = r; image = i;
}
friend complex operator+(const complex&c1,const complex&c2);
friend complex operator-(const complex&c1, const complex&c2);
friend complex operator*(const complex&c1,const complex&c2);
friend complex operator/(const complex&c1,const complex&c2);
friend void printf(const complex&c);
private:
double real, image;
};
inline complex complex::operator+(const complex& c1,const complex&c2)
{
return complex(c1.real + c2.real,c1.image + c2.image);
}
inline complex complex::operator-(const complex&c1,const complex&c2)
{
return complex(c1.real - c2.real, c1.image - c2.image);
}
inline complex complex::operator*(const complex&c1,const complex&c2)
{
return complex(c1.real*c2.real - c1.image*c2.image, c1.real*c2.image +c1.image*c2.real);
}
inline complex complex::operator/(const complex&c1,const complex&c2)
{
return complex((c1.real*c2.real + c1.image*c2.image) / (c2.real*c2.real+c2.image*c2.image),
(c1.image*c2.real - c1.real*c2.image) / (c2.real*c2.real + c2.image*c2.image));
}
6.多线程通信有哪几种方式?
全局变量、互斥量、信号量、临界区
1.全局变量
通过全局变量进行通信,要对该变量加关键字volatile
volatile(易变的):每次从内存中去读这个值,而不是因编译器优化从缓存的地方读取
2.互斥量
互斥器的功能和临界区域很相似。区别是:Mutex所花费的时间比Critical Section多的多,但是Mutex是核心对象(Event、Semaphore也是),可以跨进程使用,而且等待一个被锁住的Mutex可以设定 TIMEOUT,不会像Critical Section那样无法得知临界区域的情况,而一直死等。MFC中的对应类为CMutex。Win32函数有:创建互斥体CreateMutex() ,打开互斥体OpenMutex(),释放互斥体ReleaseMutex()。Mutex的拥有权并非属于那个产生它的线程,而是最后那个对此 Mutex进行等待操作(WaitForSingleObject等等)并且尚未进行ReleaseMutex()操作的线程
3.信号量
信号量是最具历史的同步机制。信号量是解决producer/consumer问题的关键要素。对应的MFC类是Csemaphore。Win32函数 CreateSemaphore()用来产生信号量。ReleaseSemaphore()用来解除锁定。Semaphore的现值代表的意义是目前可用的资源数,如果Semaphore的现值为1,表示还有一个锁定动作可以成功。如果现值为5,就表示还有五个锁定动作可以成功。当调用Wait…等函数要求锁定,如果Semaphore现值不为0,Wait…马上返回,资源数减1。当调用ReleaseSemaphore()资源数加1,当时不会超过初始设定的资源总数。
4.临界区
CRITICAL_SECTION是最快的。其他内核锁(事件、互斥体),每进一次内核,都需要上千个CPU周期。
使用临界区域的第一个忠告就是不要长时间锁住一份资源。这里的长时间是相对的,视不同程序而定。对一些控制软件来说,可能是数毫秒,但是对另外一些程序来说,可以长达数分钟。但进入临界区后必须尽快地离开,释放资源。如果不释放的话,会如何?答案是不会怎样。如果是主线程(GUI线程)要进入一个没有被释放的临界区,呵呵,程序就会挂了!临界区域的一个缺点就是:Critical Section不是一个核心对象,无法获知进入临界区的线程是生是死,如果进入临界区的线程挂了,没有释放临界资源,系统无法获知,而且没有办法释放该临界资源。这个缺点在互斥器(Mutex)中得到了弥补。Critical Section在MFC中的相应实现类是CcriticalSection。CcriticalSection::Lock()进入临界区,CcriticalSection::UnLock()离开临界区。
7.内存管理的重要原则有哪些?
原则1: 如果2个并列的栈要共享数据(非成员变量),则共享内存必须分配在堆Heap, 用智能指针或者GC管理.
原则2: 如果2个嵌套的栈要共享数据, 则共享内存可分配在栈Stack, 由RAII自动管理.
原则3: 如果共享数据尺寸太大,则共享内存必须分配在堆Heap, 用智能指针或者GC管理.
无需解释, 栈尺寸有限制.
原则4: 客户端应用使用智能指针, 能得到更好的响应; 服务器应用使用GC, 代码简洁, 稍微停顿无所谓.
9.列举常用编译器有哪些?
msvc、gcc、clang
上一篇: 什么才是“男人的菜”?吃了这物让你更英勇