some C++ summary

来源:互联网 发布:苹果mac激活时间查询 编辑:程序博客网 时间:2024/05/18 02:17

1, 头文件(包括.h和.hpp)里面有

   #ifndef __SOMEFILE_H__   <--if not define

   #define __SOMEFILE_H__

   ... ... //声明,定义

   #endif

加入这些是防止头文件被重复include. 需要注意的是宏(__SOMEFILE_H__)在每个头文件里面要不同,所以一般而言是大写的文件名

当该头文件多次被包含的时候,第一次#ifndeftrue,就会定义#define,以及定义里面的变量;第二次被包含的时候,判断ifndeffalse,不进到里面去执行

.h一般是定义函数,类以及宏.如果定义某个变量,那么就会有重复定义的问题。如

三个文件,main.c, common.c以及common.h, 在common.h里面定义了char key;且main.c和common.c都include该.h文件。那么就会有重复定义的问题。

所以为了在头文件避免该问题,就加入#ifndef,#define和#endif.这样即使加入某些变量也不会有问题

 

 

2, .hpp

.hpp就是.h + .cpp, 将cpp的实现代码混入.h, 即定义和实现在同一个文件。

一些注意点:

1) 不能使用全局函数和全局对象。原因是.hpp可能会被多个文件include,这样会产生重复定义

2) 类之间不能循环调用,即A调用了B,B也调用了A

3) 不能使用静态变量。

原因是:如果.hpp含有类,这个类含有静态变量.因为静态变量属于类,不属于某个对象。静态变量不能直接初始化,其初始化是这样的:

      class A
      {
       public:
          static int i;       
      };
      int A::i = 1;

      且只能初始化一次,某个会有重复定义的错误

      因此,如果在.hpp里面定义了含有静态变量的类,那么就要在该文件里面初始化。因为该hpp文件会被include多次,所以会执行初始化很多次

4) .h&.cpp可以使用usingnamespace std, .hpp不可以

所谓usingnamespace std:

C++标准库的所有标识符(cout,endl等)全部放在名称为std的namespace之下

所以有两种方式来调用:

a) std::cout<<std::hex<<3.4<<std::endl;

b) using namespace std

cout<<hex<<3.4<<endl;

c) using std::cout;using std::hex;using std::endl;

cout<<hex<<3.4<<endl;

 

 

3, this指针

   this只能在成员函数中使用

   全局函数,静态函数都不能使用this

   this在成员函数的开始前构造的,在成员的结束后清除

 

 

4, extern

   extern有两个作用:

1) 与”C”一起用。如extern “C” void fun(int a, int b);编译就会按照c语言的规则编译,否则因为C++支持重载,其名字会编译为fun@abc_int_int之类的乱码,这样函数就会找不到

2) 不与”C”一起用,用来修饰变量或函数的时候,表明该函数或变量在模块也可以用。修饰函数与变量实际上是一样的  

  

 

5, const

   1)const 与define相比,具有数据类型,编译器会对其进行检查如byte不能超过FF,但是define直接进行替换,不检查

   2)不能在类声明中初始化const成员,如

   class A

   {

      const int SIZE =100;  <--wrong

      int array[SIZE];

   }

   而应该在构造函数的初始化表的时候进行

   class A

   {

      A(int size);  //构造函数

      const int SIZE;

   }

   A::A(intsize):SIZE(size) //构造函数的初始化表

   {

     ...

   }

   A a(100) //size =100

   3) char const *p;//*p is const, p is changable

char*const p; //p is const,*p is changeable

intconst *p1,p2 //p2const,与最近的一个结合

 

 

6, static

1) 在函数内,静态的变量维持其值不变

2) 在模块内,函数外,模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

 

 

8, inline

   本来是要调用函数的,需要频繁的使用stack的。现在用上了inline之后,是直接替换,不是调用。可以节省栈空间。

   注意的是inline只适用简单的函数.根据google c++ coding style guideline, inline的函数不能超过10行

 

 

9, 变量声明与变量定义

   变量定义是指数据类型 + 变量名称,编译器分配内存

   变量声明是指extern 数据类型 + 变量名称,编译器分配内存

 

12, 内存分为stack,heap,全局区(或称静态区)(static以及全局变量),常量区(string)和代码区

NOTE:在全局区的变量的默认值为0

 

13, 颠倒一个字节, 如该字节原来的位数是1 2 3 4 5 6 7 8, -->8 7 6 5 4 3 2 1

unsigned char reverse8( unsigned charc )
{
     c = ( c & 0x55 ) << 1| ( c & 0xAA ) >> 1;
     c = ( c & 0x33 ) << 2| ( c & 0xCC ) >> 2;
     c = ( c & 0x0F ) << 4| ( c & 0xF0 ) >> 4;
     return c;
}

(1)2个2个为一组,交换前一半和后一半, 变成:   2 1 4 3 6 5 8 7
(2)4个4个为一组,交换前一半和后一半, 变成:   4 3 2 1 8 7 6 5
(3)再8个为一组,交换前一半和后一半, 变成:   8 7 6 5 4 3 2 1

 

 

14, C++四种强制转换

const_cast: 消除const标志

reinterpret_cast: 任意两个类型的指针之间可以转换,一般用于底层,导致implement-dependent

static_cast: 最接近C的风格

dynamic_cast:有两个条件,1),数据类型为指针或引用,2)要求基类是多态的(含有虚函数)

#include <iostream>

class CBase {};

class CDerived: public: CBase {};

int main()

{

  CBaseb;

 CDerived *d;

  d =dynamic_cast<CDerived *>(&b) <--wrong. CBase不含有需函数

}

可以将类的定义改为

class CBase {virtual void dummy(){} }

 

 

15,引用与取地址的区别: 引用的格式一定是type & variable; 如int &n = m; void fun(int&n, int m);

 

16,delete/free之后必须将指针置为null

 

17,eat up memory

int main()

{

  char *p = NULL;

  while (TRUE)

  {

    p = new float[10000];

    if(p == NULL)

    exit(1);   //exit(0)正常退出,exit(1)表示非正常退出,是会影响os log

  }

 return 0;

}

eat up cpu

int main()

{

  while(1)

  { };

  return 0;

}


 

18,为何malloc/free需要:C语言只支持malloc/free,不支持new/delete

   为何new/delete需要: C++要求分配内存的同时执行构造函数,释放内粗的同时执行析构函数

 

19,char *p[3]   -->括号的优先级高于*,所以p先与[结合,所以p是一个数组,数组元素皆为char *

char (*p)[3] -->p是一个指针,指向一个数组,其元素为char

 

20,char a[20];

int *p = (int*)a;

p++; --> sizeof(int) + a; //sizeof的时候要去掉一个*

 

char a[20];

char *p = a;

char **ptr = &p; //实际上ptr = p, &p = char**(&p) = char **ptr, àptr = &p;

ptr ++; //相当于&p+sizeof(char*) (将ptr的类型去掉一个*) sizeof(char*)=4, &p的值未知

 

21,thread & process

简而言之,一个程序至少有一个进程,一个进程至少有一个线程

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉

线程之间用mutex

进程之间用critical section

 

22, 重载与覆盖

成员函数被重载的特征:

(1)相同的范围(在同一个类中);(不能一个全局函数,一个成员函数)

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

 

23,成员初始化列表

classMemberInitializationList

{

private:

        int i;

        int j;

public:

        MemberInitializationList(int val) :j(val), i(j)       //j(val), i(j)就是所谓的成员初始化列表,根据变量定义的顺序,先i后j,  i=j, j=val

        {

        }

 

        inline void printinfo()

        {

                  cout << "i = " << i << ", j = " <<j << endl;

        }

};

 

int main(void)

{

        MemberInitializationList MIL(10);

        MIL.printInfo();

        

        return 0;

}

i=10,j=10

初始化列表也可以表示为.据说用初始化列表的方式效率比较高,因为没有用临时变量

MemberInitializationList(intval)

{

  j=val;

  i=j;

}

 

 

24sizeof

sizeof(a)-->sizeof(type of a)

so char *p; sizeof(p) = sizeof(char *)=4;

 

25, 基类的构造函数、析构函数、赋值函数都不能被派生类继承

 

27, 继承规则应当是:若在逻辑上B 是A 的“一种”,并且A 的所有功

能和属性对B 而言都有意义,则允许B 继承A 的功能和属性

比如鸵鸟能否从鸟类继承?鸟飞的属性对于鸵鸟无意义,因此不能继承

 

28, 继承和派生:
继承从子类的角度看。派生从基类的角度看

原创粉丝点击