C/C++语言点滴搜集

来源:互联网 发布:ubuntu 16.04分区大小 编辑:程序博客网 时间:2024/06/05 04:30

 C/C++语言点滴搜集   添加中。。。(last update 2012/05/23)

 

  • ++ 自增运算符 (int i, j)
    • i++ returns a r-value, so it can not be used in this way: i++ = j;
    • 区分a++, ++a: a++是先赋值,再自增,++a是先自增,再赋值,因此++a可作为左值,而a++不能作为左值。如:++(++a); //正确;++(a++); //错误
    • 重载: a++: operator++(int)//此处int是形参,无实际用处; ++a: operator++()
  • Exchange two int variables without including temporal variable
    • i^=j^=i^=j
    • This doesn't work for other types of variables, e.g. float, pointer
  • 数组的指针  Array's pointer 
    • How to declare: int (*p)[N];  // a pointer to an integer array of size N
                                                   // N must be a compiling-time constant
    • e.g.
          char (*c)[4];
          c = &("abs");
          int (*d)[4];
          int e[]={1,2,3,4};
          d = &e;                      // this is different than char, we cannot use  d = &([1,2,3,4]);
    • 调用二维数组 e.g.
          int array[NROWS][NCOLS];
          f(array);
          void f(int (*ap)[NCOLS])  //*ap is a pointer to an array of size NCOLS
          {
                // Need to allocate memory for *ap according to the size NROWS
          }
  • 数组做参数传入
    • 一般同时传入数组指针和数组大小
    • 另外一种机制是将参数声明为数组的引用。当参数是一个数组类型的引用时,数组长度成为参数和类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。
      // 参数为10个int的数组 // parameter is a reference to an array of 10 ints
      void putValues(int (&arr)[10]);
      int main()
      {
          int i, j[2];
          int a[10];
          putValues(i); // 错误:实参不是10个int的数组
          putValues(j); // 错误:实参不是10个int的数组
          putValues(a); // 正确
          return 0;
      }
      因为数组的长度现在是参数类型的一部分,所以putValues()的这个版本只接受10个int的数组。这限制了可以作为实参被传递给putValues()的数组的种类。但是,它也使函数的实现更加简单。
  • 虚函数表
    • 可以看如下网页
      • http://www.cppblog.com/xczhang/archive/2008/01/20/41508.html 
  • 死锁:
    • 产生条件如何防止:http://blog.csdn.net/macrossdzh/article/details/5954578
  • 用枚举常数定义真假布尔值   enum bool {false, true}  // NOT enum bool {true, false}
  • C++基本概念
        封装 encapsulation
        继承 inheritance
        重载 overload   including member function overloading, operator overloading,
        覆盖 override
        接口 interface       c++中没有Interface这个关键词,C++中Interface也是class, C++中用一个全部是纯虚函数的类来表示接口。
        虚函数与多态 virtual function and polymorphism
            虚函数只能借助于指针或者引用来达到多态,如果直接调用,则虽然是虚函数,但它不是多态的:
            class_instance.member_function();
        纯虚函数与抽象类 pure virtual function and abstract class
        非虚函数与句柄选择 function and handle
        动态绑定与静态绑定 dynamic binding and static binding
        变量命名
            In C++, the name of variable is case sensitive, so A and a are different
            Variables leading with single underscore or double underscores are usually used by the compiler.
        dynamic_cast <typename>   static_cast <typename>  const_cast <typename>
  • 面向对象编程
    • 重载与覆盖:重载overload是根据函数的参数列表来选择要调用的函数版本,
      • 重载(overload):在同一个类中,出现多个同名的方法的现象就是Overload重载事发生在同一个类中,不同方法之间的现象。
        在c++或者java中,方法一般为 返回类型 方法名(参数1,参数2) 判断2个方法是不是overload,主要指方法名一样,参数不一样,参数不一样指的是参数的个数,相同位置的参数的类型是否一样,而与参数(型参)的名称无关(参数类型/个数/顺序,不同),与返回类型也无关。程序会根据不同的参数列来确定需要调用的函数
      • 覆盖 override: Overriding 也许叫做overwriting更合适,覆盖是指在子类(c++中的派生类) 中重新定义父类的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体(花括号中的部分)不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本。我们说Sub类覆盖了父类的m1方法
        虚函数”(或者是“虚方法”)。虚函数就是允许被其子类重新定义的成员函数。而子类重新定义父类虚函数的做法,称为“覆盖”(override),或者称为“重写”。
      • 重载Overload特点(两必须一可以)
        public bool withdraw(double amt, string name)
        public double withdraw(double amt)
        1、方法名必须相同 2、参数列表必须不相同 3、返回值类型可以不相同
        注意:存在于同一类中,
      • 覆写Override特点(三相同):public override bool withdraw(...)
        1、方法名相同 2、参数列表相同 3、返回值类型相同
        注意:override存在于继继承的关系类中。但是只有虚方法和抽象方法才能被覆写.
      • 覆盖(override)和重载(overload)。上面说了,覆盖是指子类重新定义父类的虚函数的做法。而重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function   func(p:integer):integer;和function   func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是 “覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!
    • 虚函数:
    • 多态:
      • 优缺点:http://www.diybl.com/course/3_program/c++/cppjs/2008717/133457.html
        多态造成内联性能的损失才是多态真正的成本。由于内联是编译期静态行为多态则是程序执行期的动态行为,两者矛盾不能共存。内联的两大优点:一是消除了函数调用的开销,也减小内存缺页的可能性,这点比较明显;一是使得编译器能够进行深度优化,这点对程序的性能不容忽视,也比较隐秘。
      • 多态是根据运行时对象的实际类型来选择要调用的虚virtual函数版本,多态的实现是通过派生类对基类的虚virtual函数进行覆盖override来实现的,若派生类没有对基类的虚virtual函数进行覆盖override的话,则派生类会自动继承基类的虚virtual函数版本,此时无论基类指针指向的对象是基类型还是派生类型,都会调用基类版本的虚virtual函数;如果派生类对基类的虚virtual函数进行覆盖override的话,则会在运行时根据对象的实际类型来选择要调用的虚virtual函数版本,例如基类指针指向的对象类型为派生类型,则会调用派生类的虚virtual函数版本,从而实现多态。
      • 多态(polymorphism)允许将子类类型的指针赋值给父类类型的指针。多态性在Object   Pascal和C++中都是通过虚函数(Virtual   Function)实现的。
        有的说是允许将子类类型的指针赋值给父类类型的指针,多态有时候也被称为动态绑定或者晚绑定或运行时绑定,意思是编译的时候不必关心,运行的时候才决定调用哪个对象的哪个方法。我觉得多态的用途之一就是在父类提供一个接口(服务),然后调用的时候用的却是子类的具体实现。在main方法中b声明为Base类型,而实例化的时候是一个Sub类的实例,Sub中没有实现m2方法,所以,将调用Base的m2方法,而Sub overwirte了父类的m1方法,所以,b.m2()将调用Sub类的m1方法。
      • 多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!而且现实往往是,要有效重用代码很难,而真正最具有价值的重用是接口重用,因为“接口是公司最有价值的资源。设计接口比用一堆类来实现这个接口更费时间。而且接口需要耗费更昂贵的人力的时间。”

        假设我们有一个描述飞机的基类(Object   Pascal语言描述,下同): 然后,我们从plane派生出两个子类,直升机(copter)和喷气式飞机(jet):现在,我们要完成一个飞机控制系统,有一个全局的函数   plane_fly,它负责让传递给它的飞机起飞,那么,只需要这样:
                procedure   plane_fly(const   pplane   :   plane);
                begin
                        pplane.fly();
                end;
                就可以让所有传给它的飞机(plane的子类对象)正常起飞!不管是直升机还是喷气机,甚至是现在还不存在的,以后会增加的飞碟。因为,每个子类都已经定义了自己的起飞方式。可以看到   plane_fly函数接受参数的是   plane类对象引用,而实际传递给它的都是   plane的子类对象,现在回想一下开头所描述的“多态”:多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。很显然,parent   =   child;   就是多态的实质!因为直升机“是一种”飞机,喷气机也“是一种”飞机,因此,所有对飞机的操作,都可以对它们操作,此时,飞机类就作为一种接口。多态的本质就是将子类类型的指针赋值给父类类型的指针(在OP中是引用),只要这样的赋值发生了,多态也就产生了,因为实行了“向上映射”。

  • C与C++语言的区别(增加中。。。)
    • C++定了关键字bool, true, and false
    • C++定义了类class
    • C++增加了\\做行注释(注:C99中也提供了行注释)
    • C++使用const来修饰常量(常变量,函数,成员函数都可以用const修饰,但意义各不相同)
    • C++增加了引用传递(&)
    • C++增加了new/delete作为运算符来进行创建和销毁变量。(注:C中的malloc/free是全局函数#include <malloc.h>,而new/delete是运算符)
    • C++增加了inline修饰符,内联函数(注:只需要在函数实现部分标注即可,不需在声明部分;类成员函数如果定义在类声明中,自动成为内联函数)
    • C++增建了重载overload,虚函数virtual,覆盖override等OO机制下的特性
    • C++中增加了for循环的用法可以在第一部分内定义变量for(int i=0; i<10; ++i){...}(注:C99中也在C语言中增加了此项)
    • 。。。
  • sizeof()
    • void main()
      {
            char ch[]="how are you";
            char *p="how are you";
            cout<<sizeof(ch)<<" "<<sizeof(p)<<' '<<strlen("how are you")<<endl;
      }
      以上程序的运行结果为:12,4,11
      sizeof返回的是实际字节数,包含了末尾的’\ 0’,而strlen只是字符的个数。
  • NULL 与 空字符串
    • NULL = 0x000000000表示未被指定内容的指针;而空字符串是一个对象,有占内存空间,且第一个字符为 '\0' or 0
    • strcpy(str, "");//可以
      strcpy(str, (char*)(NULL));//合法但是不合理
      strcpy(str, NULL);//can compile but fail to run
      strcpy(str, '\0'); // same as empty string
      strcpy(str, 0); // same as char*p=NULL;strcpy(str,p)
  • 变量:变量类型如:int(整形),char(字符型)是用来说明变量所占用的内存空间的大小;变量存储类型用来说明变量的作用范围。
    • 变量可以分为全局变量、静态全局变量、静态局部变量和局部变量 
      • 按存储区域分:
        全局变量、静态全局变量和静态局部变量都存放在内存的全局数据区,局部变量存放在内存的栈区,动态申请数据存在于(堆)中。
      • 按作用域分:
        全局变量在整个工程文件内都有效;
        静态全局变量只在定义它的文件内有效;
        静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;
        局部变量在定义它的函数内有效,但是函数返回后失效。
      • 全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。
    • C语言的变量存储类有:自动类、寄存器类、静态类和外部类。
      • 关键字auto加在变量名及其类型前,用来说明它是自动变量。局部变量是指在函数内部说明的变量(有时也称为自动变量)。用关键字auto进行说明, 当auto省略时, 所有的非全程变量都被认为是局部变量, 所以auto实际上 从来不用。 局部变量在函数调用时自动产生,但不会自动初始化, 随函数调用的结束, 这个变量也就自动消失了, 下次调用此函数时再自动产生, 还要再赋值, 退出时又自动消失。
      • static称为静态变量。根据变量的类型可以分为静态局部变量和静态全程变量。
        1. 静态局部变量:它与局部变量的区别在于,在函数退出时,这个变量始终存在, 但不能被其它函数使用, 当再次进入该函数时, 将保存上次的结果。其它与局部变量一样。
        2. 静态全程变量:静态全程变量就是指只在定义它的源文件中可见而在其它源文件中不可见的变量。
            它与全程变量的区别是: 全程变量可以再说明为外部变量(extern), 被其它源文件使用,而静态全程变量却不能再被说明为外部的, 即只能被所在的源文件使用。
      • extern称为外部变量。为了使变量除了在定义它的源文件中可以使用外, 还要 被其它文件使用。因此, 必须将全程变量通知每一个程序模块文件, 此时可用 extern来说明。
      • 寄存器变量:在执行速度很重要的情况下使用。其思想是告诉编译程序把该变量放在一个CPU寄存器中。因为数据在寄存器中操作比在内存中快,这样就提高了程序代码的执行速度。寄存器变量的说明是在变量名及类型之前加上关键字register。值得注意的是取地址运算符&不能作用于寄存器变量。
      http://blog.csdn.net/feiyond/article/details/1650698
  • 常量const
    • 利用*为分界线,如果const跟变量名在同一侧,则指针是常量,指针内容可变;如果不再同一侧,则指针指向的内容是常量,而指针值可以变
  • 引用指针
    • http://www.cnblogs.com/skynet/archive/2010/09/22/1832911.html
      引用-引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用
      首先,引用不可以为空,但指针可以为空。定义一个引用的时候,#必须初始化#。而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。 (释放指针后要立即置空 = NULL,然后判空操作才有意义,否则面临野指针
      其次,引用不可以改变指向,对一个对象"至死不渝";但是指针可以改变指向,而指向其它对象。说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。)
      再次,引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节。
      最后,引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。
      总而言之,言而总之——它们的这些差别都可以归结为"指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。"

      常量指针VS常量引用
      常量指针:const int *i;指向常量的指针,在指针定义语句的类型前加const,表示指向的对象是常量。定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。
      常量引用:const int &u;指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。也跟指针一样不能利用引用对指向的变量进行重新赋值操作。
      指针常量VS引用常量
      在指针定义语句的指针名前加const,表示指针本身是常量。在定义指针常量时必须初始化!而这是引用天生具来的属性,不用再引用指针定义语句的引用名前加const。指针常量定义"int* const pointer=&b"告诉编译器,pointer是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pointer可以修改。
      而就不存在所谓的"常量引用常量",因为跟上面讲的一样引用变量就是引用常量。C++不区分变量的const引用和const变量的引用。程序决不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的。如果对引用应用关键字const,起作用就是使其目标称为const变量。即没有:Const double &const a=1;只有const double& a=1;
      总结:有一个规则可以很好的区分const是修饰指针,还是修饰指针指向的数据——画一条垂直穿过指针声明的星号(*),如果const出现在线的左边,指针指向的数据为常量;如果const出现在右边,指针本身为常量。而引用本身与天俱来就是常量,即不可以改变指向。

      指针传递和引用传递
          指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
          引用传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

      引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。

      引用返回值
      如果一个函数返回了引用,那么该函数的调用也可以被赋值。这里有一函数,它拥有两个引用参数并返回一个双精度数的引用:
      double &max(double &d1,double &d2)
      {
      return d1>d2?d1:d2;
      }
      由于max()函数返回一个对双精度数的引用,那么我们就可以用max() 来对其中较大的双精度数加1:
      max(x,y)+=1.0;

      给函数传递大型对象
      当大型对象被传递给函数时,使用引用参数可使参数传递效率得到提高,因为引用并不产生对象的
      副本,也就是参数传递时,对象无须复制。

      传递可变参数
      传统的c中,函数在调用时参数是通过值来传递的,这就是说函数的参数不具备返回值的能力。
      所以在传统的c中,如果需要函数的参数具有返回值的能力,往往是通过指针来实现的。
  • C++中结构(struct)与类(class)的区别:你认为结构(struct)有些什么作用,可以完全用类(class)取代么??
    • 从语法上,在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从根本上也只是个包装数据的语法机制。 
  • this指针
    • 先要理解class的意思。class应该理解为一种类型,象int,char一样,是用户自定义的类型。(虽然比int char这样build-in类型复杂的多,但首先要理解它们一样是类型)。用这个类型可以来声明一个变量,比如int x, myclass my等等。这样就像变量x具有int类型一样,变量my具有myclass类型。理解了这个,就好解释this了,my里的this 就是指向my的指针。如果还有一个变量myclass mz,mz的this就是指向mz的指针。 这样就很容易理解this 的类型应该是myclass *,而对其的解引用*this就应该是一个myclass类型的变量。通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名(为了通用也不可能固定实际的变量名),就用this这样的指针来使用变量自身。
    • this指针的用处:
      一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。例如,调用date.SetMonth(9) <===> SetMonth(&date, 9),this帮助完成了这一转换 .
    • this指针的使用:
      一种情况就是,在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;另外一种情况是当参数与成员变量名相同时,如this->n = n (不能写成n = n)。
    • this指针程序示例:
      this指针是存在与类的成员函数中,指向被调用函数所在的类实例的地址。根据以下程序来说明this指针
      #include<iostream.h>
      class Point
      {
        int x, y;
      public:
        Point(int a, int b) { x=a; y=b;}
        Void MovePoint( int a, int b){ x+=a; y+=b;}
        Void print(){ cout<<"x="<<x<<"y="<<y<<endl;}
      };
      void main( )
      {
         Point point1( 10,10);
         point1.MovePoint(2,2);
         point1.print( );
      }
      当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。
      MovePoint函数的原型应该是 void MovePoint( Point *this, int a, int b);第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见是因为这个参数在类中是隐含的。这样point1的地址传递给了this,所以在MovePoint函数中便显式的写成:
      void MovePoint(int a, int b) { this->x +=a; this-> y+= b;}
      即可以知道,point1调用该函数后,也就是point1的数据成员被调用并更新了值。
      即该函数过程可写成 point1.x+= a; point1. y + = b;
    • 关于this指针的一个精典回答:
      当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?this是一个指针,它时时刻刻指向你这个实例本身。
  • 3
  • 4
  • 5
  • 6



endend



原创粉丝点击