C/C++经典面试题总结摘选 C/C++程序员面试宝典(二)

 
==============================================================================
1.new 、 delete 、 malloc 、 free 关系

delete 会调用对象的析构函数 , 和 new 对应 free 只会释放内存, new 调用构造函数。 malloc 与 free 是 C++/C 语言的标准库函数, new/delete 是 C++ 的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free 。因此 C++ 语言需要一个能完成动态内存分配和初始化工作的运算符 new ,以及一个能完成清理与释放内存工作的运算符 delete 。注意 new/delete 不是库函数。

总结:new和delete会自动调用对象的构造与析构函数而malloc与free不会;

new和delete式C++运算符,而malloc和free是C/C++标准库函数。
=============================================================================
2.delete 与 delete [] 区别

delete 只会调用一次析构函数,而 delete[] 会调用每一个成员的析构函数。  delete 与 new 配套, delete [] 与 new [] 配套
这就说明:对于内建简单数据类型, delete 和 delete[] 功能是相同的。对于自定义的复杂数据类型, delete 和 delete[] 不能互用。
delete[] 删除一个数组, delete 删除一个指针简单来说,用 new 分配的内存用 delete 删除用 new[] 分配的内存用 delete[] 删除 delete[] 会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用 delete 时没用括号, delete 就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

总结: delete 只会调用一次析构函数,而 delete[] 会调用每一个成员的析构函数。
 
===========================================================================
 
3.C++ 有哪些性质(面向对象特点)
封装,继承和多态。

在面向对象程序设计语言中,封装是利用可重用成分构造软件系统的特性,它不仅支持系统的可重用性,而且还有利于提高系统的可扩充性;消息传递可以实现发送一个通用的消息而调用不同的方法;封装是实现信息隐蔽的一种技术,其目的是使类的定义和实现分离。
============================================================================
4. 子类析构时要调用父类的析构函数吗?
析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候 , 派生类的信息已经全部销毁了.定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数
 
=============================================================================
5. 多态,虚函数,纯虚函数

 
一 【多态】
多态的概念 :
1 指同一个函数的多种形态。
 
2多态是具有表现多种形态的能力的特征,在OO中是指,语言具有根据对象的类型以不同方式处理之,特别是重载方法和继承类这种形式的能力。
 
3多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
多态性在Object Pascal和C++中都是通过虚函数(Virtual Function) 实现的。
 
 
二 【虚函数,纯虚函数,抽象类】
 虚函数就是为多态而生的

多态的作用:继承是子类使用父类的方法,而多态则是父类使用子类的方法。
【例一】
class A
{
    public:
        A() {}
        (virtual) void print()
        {
            cout << “This is A.” << endl;
        }
};
class B : public A
{
    public:
        B() {}
        void print()
        {
            cout << “This is B.” << endl;   
        }   
};   
int main(int argc, char* argv[])
{   
    B b;
    A a;
    a = b;
    a.print; //make1
    // A &a = b; a->print();———————————-make2
    //A *a = new B();a->print();——————————–make3
    return 0;
}
这将显示:
This is B.
如果把virtual去掉,将显示:
This is A.
(make1,2,3分别是对应三种方式,调用结果是一样的)
加上virtual ,多态了,B中的print被调用了,也就是可以实现父类使用子类的方法。
 
多态性使得能够利用同一类(基类)类型的指针来引用不同类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作。把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(也就是可以调用子对象中对父对象的相关函数的改进方法)。

那么上面例子中为什么去掉virtual就调用的不是B中的方法了呢,明明把B的对象赋给指针a了啊,是因为C++定义了一组对象赋值的兼容规则,就是指在公有派生的情况下,对于某些场合,一个派生类的对象可以作为基类对象来使用,具体来说,就是下面三种情形:

Class A ;
class B:public A

1. 派生的对象可以赋给基类的对象
A a;
B b;
a = b;
2. 派生的对象可以初始化基类的引用
B b;
A &a = b;
3. 派生的对象的地址可以赋给指向基类的指针
B b;
A *a = &b;

A *a = new B();
由上述对象赋值兼容规则可知,一个基类的对象可兼容派生类的对象,一个基类的指针可指向派生类的对象,一个基类的引用可引用派生类的对象,于是对于通过基类的对象指针(或引用)对成员函数的调用,编译时无法确定对象的类,而只是在运行时才能确定并由此确定调用哪个类中的成员函数。

【例二】
#include<iostream>
using namespace std;
class A
{
    public:
        void (virtual) print(){cout << “A print”<<endl;}

    private:
};
class B : public A
{
    public:
    void print(){cout << “B print”< private:
};
void test(A &tmpClass)
{
    tmpClass.print();
}
int main(void)
{
    B b;
    test(b);
    getchar();
    return 0;
}
这将显示:
B print
如果把virtual去掉,将显示:
A print
 
函数的联编:在编译或运行将函数调用与相应的函数体连接在一起的过程。
1 先期联编或静态联编:在编译时就能进行函数联编称为先期联编或静态联编。
2 迟后联编或动态联编:在运行时才能进行的联编称为迟后联编或动态联编。
那么联编与虚函数有什么关系呢,当然,造成上面例子中的矛盾的原因就是代码的联编过程采用了先期联编,使得编译时系统无法确定究竟应该调用基类中的函数还是应该调用派生类中的函数,要是能够采用上面说的迟后联编就好了,可以在运行时再判断到底是哪个对象,所以,virtual关键字的作用就是提示编译器进行迟后联编,告诉连接过程:“我是个虚的,先不要连接我,等运行时再说吧”。
那么为什么连接的时候就知道到底是哪个对象了呢,这就引出虚函数的原理了:当编译器遇到virtual后,会为所在的类构造一个表和一个指针,那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址.指针叫做vptr,指向那个表。而这个指针保存在相应的对象当中,也就是说只有创建了对象以后才能找到相应虚函数的地址。
【注意】
1为确保运行时的多态定义的基类与派生类的虚函数不仅函数名要相同,其返回值及参数都必须相同,否则即使加上了virtual,系统也不进行迟后联编。
2 虚函数关系通过继承关系自动传递给基类中同名的函数,也就是上例中如果A中print有virtual,那么 B中的print即使不加virtual,也被自动认为是虚函数。
3 没有继承关系,多态机制没有意义,继承必须是公有继承。
4 如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的,如果发现基类提供了虚函数,那么最好override它。

纯虚函数:
虚函数的作用是为了实现对基类与派生类中的虚函数成员的迟后联编,而纯虚函数是表明不具体实现的虚函数成员,即纯虚函数无实现代码。其作用仅仅是为其派生类提过一个统一的构架,具体实现在派生类中给出。
一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。
抽象类:
含有一个或多个纯虚函数的类称为抽象类。
【例三】
#include<iostream>

using namespace std;
class A
{
    public:
        virtual float print() = 0;
    protected:
        float h,w;
    private:
};
class B : public A
{
    public:
        B(float h0,float w0){h = h0;w = w0;}
        float print(){return h*w;}
    private:
};
class C : public A
{
    public:
        C(float h0,float w0){h = h0;w = w0;}
        float print(){return h*w/2;}
    private:
};

int main(void)
{
    A *a1,*a2;
    B b(1,2);
    C c(1,2);
    a1 = &b;
    a2 = &c;
    cout << a1->print()<endl;
    cout << a2->print()<endl;
}
 
【注意】
1 抽象类并不能直接定义对象,只可以如上例那样声明指针,用来指向基类派生的子类的对象,上例中的A *a1,*a2;改为 A a1,a2;是错误的。
2 从一个抽象类派生的类必须提供纯虚函数的代码实现或依旧指明其为派生类,否则是错误的。
3 当一个类打算被用作其它类的基类时,它的析构函数必须是虚的。
【例三】
class A
{
    public:
        A() { ptra_ = new char[10];}
        ~A() { delete[] ptra_;} // 非虚析构函数
    private:
        char * ptra_;
};

class B: public A
{
    public:
        B() { ptrb_ = new char[20];}
        ~B() { delete[] ptrb_;}
    private:
        char * ptrb_;
};

void foo()
{
    A * a = new B;
    delete a;
}
在这个例子中,程序也许不会象你想象的那样运行,在执行delete a的时候,实际上只有A::~A()被调用了,而B类的析构函数并没有被调用! 如果将上面A::~A()改为virtual,就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。纯虚的析构函数并没有什么作用,是虚的就够了。通常只有在希望将一个类变成抽象类(不能实例化的类),而这个类又没有合适的函数可以被纯虚化的时候,可以使用纯虚的析构函数来达到目的。

最后通过一个例子说明一下抽象类,纯虚函数以及多态的妙用吧:
我们希望通过一个方法得到不同图形面积的和的方式:
#include<iostream>
using namespace std;
class A //定义一个抽象类,用来求图形面积
{
    public:
        virtual float area() = 0;//定义一个计算面积的纯虚函数,图形没确定
                    //不能确定具体实现
    protected:
        float h,w; //这里假设所有图形的面积都可以用h和w两个元素计算得出

};
class B : public A //定义一个求长方形面积的类
{
    public:
        B(float h0,float w0){h = h0;w = w0;}
        float area (){return h*w;}//基类纯虚函数的具体实现
 
};
class C : public A //定义一个求三角形面积的类
{
    public:
        C(float h0,float w0){h = h0;w = w0;}
        float area (){return h*w/2;}//基类纯虚函数的具体实现
    
};

float getTotal(A *s[],int n)//通过一个数组传递所有的图形对象
//多态的好处出来了吧,不是多态,不能用基类A调用
//参数类型怎么写,要是有100个不同的图形,怎么传递
{
    float sum = 0;
    for(int i = 0;i < n; i++)
        sum = sum + s[i]->area();
    return sum;
}
int main(void)
{
    float totalArea;
    A *a[2];
    a[0] = new B(1,2); //一个长方形对象
    a[1] = new C(1,2);//一个三角形对象
    totalArea = getTotal(a , 2);//求出两个对象的面积和
    
    return 0;

}


===============================================================================


6. 求下面函数的返回值(微软)
int func(x)
{
int countx = 0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}

假定 x = 9999 。 答案: 8

思路:将 x 转化为 2 进制,看含有的 1 的个数。
=================================================================================
 
7. 将 “ 引用 ” 作为函数参数有哪些特点?
( 1 )传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

( 2 )使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

( 3 )使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用 “* 指针变量名 ” 的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
===================================================================================
8. 在什么时候需要使用 “ 常引用 ” ?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式: const 类型标识符 & 引用名 = 目标变量名;

例 1

int a ;
const int &ra=a;
ra=1; // 错误
a=1; // 正确

例 2

string foo( );
void bar(string & s);

那么下面的表达式将是非法的:

bar(foo( ));
bar(“hello world”);

原因在于 foo( ) 和 “hello world” 串都会产生一个临时对象,而在 C++ 中,这些临时对象都是 const 类型的。因此上面的表达式就是试图将一个 const 类型的对象转换为非 const 类型,这是非法的。引用型参数应该在能被定义为 const 的情况下,尽量定义为 const 。
=====================================================================================
9. 将 “ 引用 ” 作为函数返回值类型的格式、好处和需要遵守的规则
格式:类型标识符 & 函数名(形参列表及类型说明) { // 函数体 }

好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生 runtime error! 注意事项:

( 1 )不能返回局部变量的引用。这条可以参照 Effective C++[1] 的 Item 31 。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了 ” 无所指 ” 的引用,程序会进入未知状态。

( 2 )不能返回函数内部 new 分配的内存的引用。这条可以参照 Effective C++[1] 的 Item 31 。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部 new 分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由 new 分配)就无法释放,造成 memory leak 。

( 3 )可以返回类成员的引用,但最好是 const 。这条原则可以参照 Effective C++[1] 的 Item 30 。主要原因是当对象的属性是与某种业务规则( business rule )相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

( 4 )流操作符重载返回值申明为 “ 引用 ” 的作用:

流操作符 << 和 >> ,这两个操作符常常希望被连续使用,例如: cout << “hello” << endl;  因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个 << 操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用 << 操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是 C++ 语言中引入引用这个概念的原因吧。 赋值操作符 = 。这个操作符象流操作符一样,是可以连续使用的,例如: x = j = 10; 或者 (x=10)=100;
赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

==================================================================================


10. C++ 中的 class 和 struct 的区别
从语法上,在 C++ 中(只讨论 C++ 中)。 class 和 struct 做类型定义时只有两点区别:
(一)默认继承权限。如果不明确指定,来自 class 的继承按照 private 继承处理,来自 struct 的继承按照 public 继承处理;
(二)成员的默认访问权限。 class 的成员默认是 private 权限, struct 默认是 public 权限。
除了这两点, class 和 struct 基本就是一个东西。语法上没有任何其它区别。

不能因为学过 C 就总觉得连 C++ 中 struct 和 class 都区别很大,下面列举的说明可能比较无聊,因为 struct 和 class 本来就是基本一样的东西,无需多说。但这些说明可能有助于澄清一些常见的关于 struct 和 class 的错误认识:
( 1 )都可以有成员函数;包括各类构造函数,析构函数,重载的运算符,友元类,友元结构,友元函数,虚函数,纯虚函数,静态函数;
( 2 )都可以有一大堆 public/private/protected 修饰符在里边;
( 3 )虽然这种风格不再被提倡,但语法上二者都可以使用大括号的方式初始化:
A a = {1, 2, 3}; 不管 A 是个 struct 还是个 class ,前提是这个类 / 结构足够简单,比如所有的成员都是 public 的,所有的成员都是简单类型,没有显式声明的构造函数。
( 4 )都可以进行复杂的继承甚至多重继承,一个 struct 可以继承自一个 class ,反之亦可;一个 struct 可以同时继承 5 个 class 和
5 个 struct ,虽然这样做不太好。
( 5 )如果说 class 的设计需要注意 OO 的原则和风格,那么没任何理由说设计 struct 就不需要注意。
( 6 )再次说明,以上所有说法都是指在 C++ 语言中,至于在 C 里的情况, C 里是根本没有 “class” ,而 C 的 struct 从根本上也只是个包装数据的语法机制。


最后,作为语言的两个关键字,除去定义类型时有上述区别之外,另外还有一点点: “class” 这个关键字还用于定义模板参数,就像 “typename” 。但关键字 “struct” 不用于定义模板参数。

在模版中,类型参数前面可以使用class或typename,如果使用struct,则含义不同,struct后面跟的是“non-type template parameter”,而class或typename后面跟的是类型参数。
 
关于使用大括号初始化
class 和 struct 如果定义了构造函数的话,都不能用大括号进行初始化
如果没有定义构造函数, struct 可以用大括号初始化。
如果没有定义构造函数,且所有成员变量全是 public 的话,class 可以用大括号初始化。
关于默认访问权限
class 中默认的成员访问权限是 private 的,而 struct 中则是 public 的。
关于继承方式
class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
 class 中有个默认的 this 指针, struct 没有
==================================================================================
11. 有关重载函数

返回值类型不同构不成重载
参数参数顺序不同能构成重载

c++ 函数同名不同返回值不算重载!函数重载是忽略返回值类型的。

成员函数被重载的特征有:
1) 相同的范围(在同一个类中);
2) 函数名字相同;
3) 参数不同;
4) virtual 关键字可有可无。

5) 成员函数中 有无 const ( 函数后面 ) 也可判断是否重载
========================================================================================
 
 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.static有什么用途?(请至少说明两种) 1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用 2.引用与指针有什么区别? 1) 引用必须被初始化,指针不必。 2) 引用初始化以后不能被改变,指针可以改变所指的对象。 3) 不存在指向空值的引用,但是存在指向空值的指针。 3.描述实时系统的基本特性 在特定时间内完成特定的任务,实时性与可靠性。 4.全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 全局变量储存在静态数据库,局部变量在堆栈。 5.什么是平衡叉树? 左右子树都是平衡叉树 且左右子树的深度差值的绝对值不大于1。 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。 7.什么函数不能声明为虚函数? constructor函数不能声明为虚函数。 8.冒泡排序算法的时间复杂度是什么? 时间复杂度是O(n^2)。 9.写出float x 与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001) 10.Internet采用哪种网络协议?该协议的主要层次结构? Tcp/Ip协议 主要层次结构为: 应用层/传输层/网络层/数据链路层/物理层。 11.Internet物理地址和IP地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析協議) 12.IP地址的编码分为哪俩部分? IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。 13.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。 循环链表,用取余操作做 14.不能做switch()的参数类型是: switch的参数不能为实型。 1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分) int a = 4; (A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++); a = ? 答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a; 改后答案依次为9,10,10,11 2.某32位系统下, C++程序,请计算sizeof 的值(5分). char str[] = “http://www.ibegroup.com/” char *p = str ; int n = 10; 请计算 sizeof (str ) = ?(1) sizeof ( p ) = ?(2) sizeof ( n ) = ?(3) void Foo ( char str[100]){ 请计算 sizeof( str ) = ?(4) } void *p = malloc( 100 ); 请计算 sizeof ( p ) = ?(5) 答:(1)17 (2)4 (3) 4 (4)4 (5)4 3. 回答下面的问题. (4分) (1).头文件中的 ifndef/define/endif 干什么用?预处理 答:防止头文件被重复引用 (2). #i nclude 和 #i nclude “filename.h” 有什么区别? 答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。 (3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? 答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变 量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调 用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。 (4). switch()中不允许的数据类型是? 答:实型 4. 回答下面的问题(6分) (1).Void GetMemory(char **p, int num){ *p = (char *)malloc(num); } void Test(void){ char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 请问运行Test 函数会有什么样的结果? 答:输出“hello” (2). void Test(void){ char *str = (char *) malloc(100); strcpy(str, “hello”); free(str); if(str != NULL){ strcpy(str, “world”); printf(str); } } 请问运行Test 函数会有什么样的结果? 答:输出“world” (3). char *GetMemory(void){ char p[] = "hello world"; return p; } void Test(void){ char *str = NULL; str = GetMemory(); printf(str); } 请问运行Test 函数会有什么样的结果? 答:无效的指针,输出不确定 5. 编写strcat函数(6分) 已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc); 其中strDest 是目的字符串,strSrc 是源字符串。 (1)不调用C++/C 的字符串库函数,请编写函数 strcat 答: VC源码: char * __cdecl strcat (char * dst, const char * src) { char * cp = dst; while( *cp ) cp++; /* find end of dst */ while( *cp++ = *src++ ) ; /* Copy src to end of dst */ return( dst ); /* return dst */ } (2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值? 答:方便赋值给其他变量 6.MFC中CString是类型安全类么? 答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换 7.C++中为什么用模板类。 答:(1)可用来创建动态增长和减小的数据结构 (2)它是类型无关的,因此具有很高的可复用性。 (3)它在编译时而不是运行时检查数据类型,保证了类型安全 (4)它是平台无关的,可移植性 (5)可用于基本数据类型 8.CSingleLock是干什么的。 答:同步多个线程对一个数据类的同时访问 9.NEWTEXTMETRIC 是什么。 答:物理字体结构,用来设置字体的高宽大小 10.程序什么时候应该使用线程,什么时候单线程效率高。 答:1.耗时的操作使用线程,提高应用程序响应 2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。 3.多CPU系统中,使用线程提高CPU利用率 4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独 立的运行部分,这样的程序会利于理解和修改。 其他情况都使用单线程。 11.Windows是内核级线程么。 答:见下一题 12.Linux有内核级线程么。 答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两 种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序 中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一 种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部 需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支 ,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线 程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不 到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占 用了更多的系统开支。 Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程 13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中? 答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理 堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上 14.使用线程是如何防止出现大的波峰。 答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提 高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队 等候。 15函数模板与类模板有什么区别? 答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。 16一般数据库若出现日志满了,会出现什么情况,是否还能使用? 答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记 录日志。也就是说基本上处于不能使用的状态。 17 SQL Server是否支持行级锁,有什么好处? 答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据 的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不 被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。 18如果数据库满了会出现什么情况,是否还能使用? 答:见16 19 关于内存对齐的问题以及sizof()的输出 答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能 地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问 ;然而,对齐的内存访问仅需要一次访问。 20 int i=10, j=10, k=3; k*=i+j; k最后的值是? 答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低 21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现? 答:将操作多个表的操作放入到事务中进行处理 22.TCP/IP 建立连接的过程?(3-way shake) 答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。   第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状 态,等待服务器确认; 第次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个 SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;   第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1) ,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 23.ICMP是什么协议,处于哪一层? 答:Internet控制报文协议,处于网络层(IP层) 24.触发器怎么工作的? 答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT 、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数 据的处理必须符合由这些SQL 语句所定义的规则。 25.winsock建立连接的主要实现步骤? 答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept() 等待客户端连接。 客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv( ),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。 服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连 接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock et()关闭套接字。 26.动态连接库的两种方式? 答:调用一个DLL中的函数有两种方法: 1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数 ,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向 系统提供了载入DLL时所需的信息及DLL函数定位。 2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的 出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了 。 27.IP组播有那些好处? 答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧 消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包 到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无 论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播 技术的核心就是针对如何节约网络资源的前提下保证服务质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值