欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

某公司部分面试题

程序员文章站 2022-06-04 16:39:50
...

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

相关标签: C/C++