C++一些重要概念(转)

Posted on 2008-09-06 12:57  少林  阅读(1217)  评论(0编辑  收藏  举报

C++语言中的一些概念及他们之间的区别(需要深刻理解):
   (1)  局部变量 全局变量  静态变量 const常量 寄存器变量 宏定义的常量 static变量
         (注:包括它们的内存分配,作用域,初始化等)
         局部变量: 指在函数(过程)内部定义的变量 作用域为定义该变量的函数  内存分配: 在栈中按地址从高到低分配;.
         全局变量: 通常在文件开头定义(理想位置),当这些函数以及同一个程序中的其他源程序文件中的某些函数需   要使用该全局变量时 在函数内部对该变量使用extern加以说明他是外部的;   作用域: 在程序的执行过程中一直有效; 初始化: 定义时不做初始化则系统将自动为起赋值数值型为0    内存分配: 从静态储存区域分配.
         静态变量: 用static语句可将变量声明为静态变量;  作用域是当前的子程序模块内, 而它的生存期是到整个总程序的结束; 内存分配: 全局/静态存储区;
         寄存器类型:使用关键字register声明寄存器类型的目的是将所声明的变量放入寄存器内,从而加快程序的运行速度.
        试题8:请说出static和const关键字尽可能多的作用

  解答:

  static关键字至少有下列n个作用:

  (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

  (2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

  (3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

  (4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

  (5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

  const关键字至少有下列n个作用:

  (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;

  (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;

  (3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

  (4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;

  (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如: const classA operator*(const classA& a1,const classA& a2);
  operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错: classA a, b, c;
(a * b) = c; // 对a*b的结果赋值
  操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。
  剖析:
  惊讶吗?小小的static和const居然有这么多功能,我们能回答几个?如果只能回答1~2个,那还真得闭关再好好修炼修炼。

    (2)  malloc/new, free/delete 之间的区别
          malloc,free 是C语言的标准函数库,new/delete 是C++的运算符,他们都可以动态申请内存和释放内存;
          对于非内部数据类型的对象而言, 光用malloc/free无法满足动态对象的需求. 对象要创建的同时要自动执行构造函数,对象在消亡之前要执行析构函数,由于malloc/free不是运算符, 不在编译器控制权限之内,不能把构造函数和析构函数的任务强加于malloc/free上. 因此,C++语言需要一个动态内存分配和初始化的运算符new和一个能完成清理与释放内存的运算符delete
         我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
         既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
  malloc/free功能还有一好处,就是可以和realloc组合使用,在需要扩大内存块时不一定会导致内存移动;而用new/delete实现时只能用new[]-copy-delete[]操作序列完成,每次都会导致内存移动。
   (3)  内联函数宏定义区别, 各有什么优点.
         C++语言支持内联函数,目的就是为了提高函数的执行效率.
         内联函数直接将代码插入调用外,从而减少了普通函数调用时的资源消耗, 要对参数类型进行检查;而宏不是函数,只是在编译前(预编译处理之前)将程序中有关字符串替换成宏体,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程, 不对参数类型进行检查;
         两者都能提高运行效率,但使用宏代码最大的缺点是容易出错,对于C++ 而言,使用宏代码还有另一种缺点:无法操作类的私有数据成员; C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外。

   (4)内存分配有哪几种形式?分别为何?区别是什么?对编译速度影响是何?
           内存分配有三种方式:
          从静态存储区域分配, 内存在编译时就已分配好, 这块内存在程序整个运行期间都存在, 如:全局变量,STATIC变量.
           在栈上分配, 如函数的局部变量可以在栈上分配,函数结束时自己被释放,栈内存分配的运算内置于处理器的指令集中,效率高,内存分配是连续的,,栈是向低地址扩展的数据结构,但容量有限;
          从堆上分配,亦即动态内存分配, 程序运行时用New/Malloc分配, 程序员自己负责何时用Delete/Free释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.一旦某一节点从链中断开,我们要人为的把所断开的节点从内存中释放.动态内存的生存期由我们决定,使用灵活(堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存),但问题也多
           申请效率的比较:
           栈由系统自动分配,速度较快。但程序员是无法控制的。
           堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
   (5)什么是多态(Polymorphisn) ?举个例子试试
            按字面的意思就是“多种形状”。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数(Virtual Function) 实现的。
           多态性是允许将父对象设置成为和一个和多个它的子对象相等的技术,比如Parent:=Child; 赋值之后,父对象就可以根据当前赋值给它的子对象的特性已不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。  
           简单的说,建立一个父类的变量,它的内容可以是这个父类的,也可以是它的子类的,当子类拥有和父类同样的函数,当使用这个变量调用这个函数的时候,定义这个变量的类,也就是父类,里的同名函数将被调用,当在父类里的这个函数前加virtual关键字,那么子类的同名函数将被调用

class A {
public:
A() {}

virtual void foo() {
cout << "This is A." << endl;
}
};

class B : public A {
public:
B() {}

void foo() {
cout << "This is B." << endl;
}
};

int main(int argc, char* argv[]) {

A *a = new B();
a->foo();

return 0;
}

这将显示:
This is B.

如果把virtual去掉,将显示:
This is A.  

     (6)struct 和class有什么区别?c语言中的struct 和c++中的struct一样么?有什么区别?
            (一)默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照publ                     ic继承处理;
            (二)成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。
             如果没有多态和虚拟继承,在C++中,struct和class的存取效率完全相同!简单的说就是,存取class的data member和非virtual function效率和struct完全相同!不管该data member是定义在基类还是派生类的。

     (7)说说什么是野指针?野指针什么情况下出现?(没有初始化,delete后没有赋值为NULL)
             野指针不是NULL指针,是指向垃圾内存的指针,也就是指向不可用内存的指针,通常对这种指针进行操作会  产生不可知道的错误,人们一般不会用错NULL指针,但是野指针很危险,IF对它不起作用;于出现的情况有两种:没有初始化和delete/free没有赋值为NULL.

      (8)你熟悉预编译指令么?条件编译是用来做什么的?你会写么?
              预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理.
       下面是部分预处理指令:

        指令             用途
         #           空指令,无任何效果
         #include    包含一个源代码文件
         #define     定义宏
         #undef      取消已定义的宏
         #if         如果给定条件为真,则编译下面代码
         #ifdef      如果宏已经定义,则编译下面代码
         #ifndef     如果宏没有定义,则编译下面代码
         #elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
         #endif      结束一个#if……#else条件编译块
         #error      停止编译并显示错误信息

         条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
        如:
       为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。例如:
        /*my.h*/
        #ifndef MY_H
        #define MY_H
          ……
        #endif


       1.#if指令
        #if指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif或#endif为止;否则就不编译。
    2.#endif指令
        #endif用于终止#if预处理指令。

            #define DEBUG 0
            main()
            {
                #if DEBUG
                    printf("Debugging\n");
                #endif
                    printf("Running\n");
            }

        由于程序定义DEBUG宏代表0,所以#if条件为假,不编译后面的代码直到#endif,所以程序直接输出Running。
        如果去掉#define语句,效果是一样的。

Copyright © 2024 少林
Powered by .NET 8.0 on Kubernetes