###C/C++笔记

点击查看Evernote原文

#@author:       gr
#@date:         2014-07-20
#@email:        forgerui@gmail.com

c/c++笔记。
Life is too short to learn C++.程序员

1、malloc与calloc区别

1. malloc不初始化分配的内存,已分配的内存中能够是任意的值. 
    calloc 初始化已分配的内存为0

 2. malloc返回的是一个对象
    calloc返回的是一个数组

2、static做用

  1. 限制了变量的做用域
  2. 改变了变量的存储域

3、内存释放

1. new --> delete
 2. malloc --> free

4、栈和堆

1. 栈是由编译器自动分配释放的,堆是程序员本身申请释放
 2. 栈有默认大小(1M),是连续的空间,比堆要空间小,堆不连续,空间大
 3. 申请 栈: int a;比较快 堆: malloc(1024); new() 比较慢

5、size_t

1. size_t是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int。 
 2. 在C++中,设计 size_t 就是为了适应多个平台的 。size_t的引入加强了程序在不一样平台上的可移植性。

6、sizeof

1. char *p; sizeof(p); sizeof(*p);  //p指针的大小为4,*p的大小是char型,为1

7、explicit

1. C++中的explicit每每用来修饰构造函数包括拷贝构造函数,通常构造函数有两种功能,1.是构造器,2.是隐藏且默认的类型转换操做符。为使构造函数只作为构造器,声明为explicit,则必须显式调用构造函数才行,即A a(),不能使用A a = 1。

8、纯虚函数

1. 若是函数中有纯虚函数就是抽象类,须要子类进行重写才能进行实例化,而只有virtual成员函数的话,能够实例化。

9、C++中的静态绑定和动态绑定

1. 静态绑定:在声明时采用的类型,发生在编译期。
 2. 动态绑定:目标所指的类型;在运行期决定。 
 3. 静态类型在声明时就肯定了,没法修改;动态绑定能够在程序中修改,指向其它类型。
 4. no-virtual函数是静态绑定的,即非虚函数根据声明调用;virtual函数是动态绑定的,即虚函数是根据定义决定的。
 5. 只有虚函数才使用的是动态绑定,其余的所有是静态绑定。目前我尚未发现不适用这句话的,若是有错误,但愿你能够指出来。
 6. 当缺省参数和虚函数一块儿出现的时候状况有点复杂。虚函数是动态绑定的,可是为了执行效率,缺省参数是静态绑定的。

10、C++中的声明和定义

1. 能够屡次声明,但只能定义一次
 2. 在函数外部声明同时会定义,在函数内部声明不会定义。
 3. 在函数外部可使用extern int a;表示只声明不定义。但extern int a = 1;这样也会定义。

11、const问题

有下面的一段代码:算法

class Rational{
 
private:
    int numer, deno;
public:
    Rational(int n = 0, int d = 1);
    int num()const {return numer;}
    int den() {return deno;}
};
 
Rational operator* (const Rational& l, const Rational& r){
    return Rational(l.num() * r.num(), l.den() * r.den());
}

l.num()r.num()是没问题的,而l.den()r.den()是存在问题的。由于num()是个const,而den不是const,当把对象看成pass-by-reference-to-const时,它里面的操做必须保证不得改变成员的值,因此,这个函数中调用对象的成员函数也必须是const的,因此要把den()函数改成const函数。centos

const在C语言中是表示道义上保证变量的值不会被修改,并不能实际阻止修改,经过指针能够修改常变量的值,可是会出现一些不可知的结果。几种状况不一样,咱们一个一个来看。数组

一、直接赋值安全

const int a = 3;
a = 5;
// const.c:6:2: error: assignment of read-only variable ‘a’
这种状况不用多说,编译错。

二、使用指针赋值,@孙健波提到的方法,在gcc中的warning,g++中出现error,是由于代码写得不对,由非const的变成const不用显式的转换,const变为非const须要显式转换,这种状况应当使用显式的类型转换。数据结构

const int a = 3;
int* b = (int*) &a;

printf("a = %d, *b = %d\n", a, *b);
*b = 5;
printf("a = %d, *b = %d\n", a, *b);

运行结果(注:使用msvc编译的结果一致):多线程

$ gcc const.c
$ ./a.out
a = 3, *b = 3
a = 5, *b = 5

$ g++ const.cpp
$ ./a.out
a = 3, *b = 3
a = 3, *b = 5

这里使用g++编译时,a的值之因此没有改变,是由于编译时a是常量,而后被编译器编译为当即数了。所以对使用b指针修改不会更改a的值。app

值得注意的是,若是a被定义为全局常变量,使用指针修改会引起segment fault。dom

在函数的原型中,咱们也经常使用const修饰指针,表示函数的实现者在道义上不会去修改这个指针所指向的空间。例如咱们熟知的strcpy函数,原型以下:

char* strcpy(char* dst, const char* src);

传入的参数src类型是const char*,表示函数内部实现不会修改src所指向的空间。之因此说是道义上,是由于在内部经过上述指针强制类型转换的方式能够修改该空间的值。另外,若是咱们声明了咱们不会修改传入指针的所指向的空间,那么咱们也不该当去修改这块空间,由于这个传入的指针可能会是一个不可写的内存,而后出现段错误。

12、构造函数的默认参数

当类的构造函数有默认参数时,即可以匹配<=参数个数的构造函数,没有给出的参数使用默认值初始化。

十3、当以pass-by-value传递参数时,须要考虑拷贝构造函数是否正确。

十4、类体中的函数自动内联

类体中定义的成员函数,若是简单,编译器会自动变为内联函数。

十5、头文件(详见 Topic 二十二)

在头文件能够声明变量,不能够定义变量,即必须加上extern,不然一旦这个头文件被两个源文件#include,便会报重定义的错误(由于#include后便会在多处定义)。若是实在想定义一个全局变量,应该在cpp中定义,这样才不会报错。头文件的做用是提供声明,方便别人进行编译,切不可在头文件中进行定义。
但能够在头文件中定义下面三个东西:类,const变量(只初始化一次),内联函数, 模板函数。

十6、头文件与重复定义

头文件卫士不能解决变量重定义的问题,它的目的是要保护嵌套的包含指令中的内部连接属性的名称不被重复定义。它避免了在一个程序文件中重复include两次,但在另外一个程序文件中仍是能够include这个头文件,因此若是在头文件定义变量,即便使用了头文件卫士,仍是会有重定义的问题。

十7、OpenCV的Debug与Release

一直读入图片出错,把Debug修改为Release版本就解决了,估计是OpenCV中的dll调试与运行版本问题。

十8、二维数组delete

二维数组的delete要进行两次删除。

// Definition
double** m_matrix;
// Release
if ( m_matrix != NULL ) {
    // free arrays
    for ( unsigned int i = 0 ; i < m_rows ; i++ ) {
        delete [] m_matrix[i];
    }
    delete [] m_matrix;
}
m_matrix = NULL:

十9、 matrix(x, y)的实现

定义一个Matrix类,初始化对象Matrix matrix;实现下面的两个操做。

//两种功能, 一个是赋值,一个是取值
matrix(x, y) = a;
cout << matrix(x,y);

实现须要使用operator ()操做符。

//赋值,返回的是引用,能够修改其值
template <typename T>
inline T& operator () (int i, int j){
    return matrix[i][j];
}
//取值,返回的是const
template <typename T>
inline const T& operator() (int i, int j) const{
    return matrix[i][j];
}

二10、 list vs vector

vector 可使用下标访问,list 不能用下标的形式访问。
vector遍历删除:

vector<Track>::iterator it1 = trajs.begin();
while (it1 != trajs.end()){
    if (it1->isLost()){
        trajs.erase(it1);
    }else{
        it1++;
    }
}

list 遍历删除:

vector<Track>::iterator it1 = trajs.begin();
while (it1 != trajs.end()){
    if (it1->isLost()){
        // For the vector erase the element, so it1--
        cout<<"delete: "<<it1->getId()<<endl;
        it1 = trajs.erase(it1);
    }else{
        it1++;
    }
}

二11、sprintf,sscanf

sprintf:把变量打印到字符串中,从而得到数字的字符形式(能够实现将整形转换成字符型)

int value = 10;
char str[128];
// 结果为: "the result of value is 10",将数值转换为字符
sprintf(str, "the result of value is %d", value);

sscanf: 从一个字符串中读进与指定格式相符的数据. 格式能够是整型数据等。

//str="ab",取到非a-z为止
sscanf("ab123D", "%[a-z]", str);
//str=空,取到a-z为止,由于第一个就是a,因此不取
sscanf("ab123D", "%[^a-z]", str);
//str="DE12",取到a-z为止
sscanf("DE12ab123D", "%[^a-z]", str);

二12、头文件

归纳: 将声明放在头文件中,实现放在cpp中。

  • 全局变量,全局函数声明放在头文件中,实现放在cpp中。
  • 将类、结构体的声明,定义放在头文件中。
  • 类成员函数声明放在头文件中,实现放在cpp中。
  • inline相关的函数、template相关的函数和类 实现放在头文件中。
  • static const修饰的变量的初始化放在头文件中,类中static变量的初始化放在cpp中,const的初始化放在初始化列表中。

二十3、比较与赋值耗时

比较耗时长
比较有一个读a和b值得过程,而后比较要创建一个新的区间存储比较结果,
而复制只是读取b的值,而后存储到a的位置,根本不须要读取a原来的值。

二十4、奇怪的undefined reference

typedef struct  {
    int nFeatures;
    KLT_Feature *feature;
    void WriteToImage(const std::string imgName, const cv::Mat &img) const;
}  KLT_FeatureListRec, *KLT_FeatureList;

定义的结构体有KLT_FeatureListRec*KLT_FeatureList,但其中成员函数WriteToImage实如今cpp文件中,以下使用会形成undefined reference

KLT_FeatureList fl;
fl->WriteToImage(std::string(filename),img);

但把WriteToImage的实现放在struct里,这时即可以正常编译。应该是KLT_FeatureList没法匹配到KLT_FeatureListRec的成员函数。

二十5、free,delete

free以后变为野指针,要置为NULL

二十6、默认析构函数

默认析构函数不会释放类型为内置指针的成员变量指向的空间。

二十7、默认拷贝构造函数

  1. 默认拷贝构造函数对指针类型进行地址复制,即多个指针指向同一个地址,若是须要拷贝到新的空间,须要本身实现拷贝构造函数。
  2. 拷贝构造时,复制“对象内的全部成员变量” 及 “全部base成分”,能够经过Child::operator=(const Parent& rhs)去调用子类拷贝构造函数,复制base成分

    class Children{
     private:
         int x;
     public:
         Children& operator=(const Children& rhs){
             x = rhs.x;
             return *this;
         }
     };
     class Parent : public Children{
     private:
         int y;
     public:
         Parent& operator=(const Parent& p){
             y = rhs.y;
             //必定要!!!调用子类operator=,拷贝base成分
             Children::operator=(p);
             return *this;
         }
     };

二十8、类成员变量的初始化

  1. 普通变量能够在初始化列表初始或在构造函数中赋值
  2. const变量只能在初始化列表中初始化。
  3. static变量只能在类外初始化,且不能在函数中定义,必须在全局定义。若是不初始化,直接使用就只有声明没有定义,会报错,vs中会报没法解析的外部符号。
  4. const static静态常量能够直接在类的定义中初始化
  5. Reference类型在初始化列表中初始化。

有三种包括引用const基类要经过初始化列表初始化。

注意:
C++11中能够为数据成员提供一个类内初始值,便可以在类中直接初始化数据成员,没有初始值的成员将被默认初始化。

二十9、拷贝构造函数

拷贝构造函数第一个参数是自身类类型(且必须是引用类型,传值会致使死循环),后面的参数都应该提供默认值。

三10、拷贝构造、拷贝赋值

拷贝构造函数和赋值操做符之间不能相互转换,若是调用的函数没有实现,则会调用编译器默认生成的函数。

A a = b;    //调用的是拷贝构造,等同于A a(b);
A a;
a = b;      //调用的是拷贝赋值

三11、 cout char*

若是想要输出指针地址,使用static_cast<const void*>转型。

char* str = "Hello world";
cout << str << endl;        //输出的是字符串,不是地址
cout << static_cast<const void *>str << endl;           //输出字符指针的地址

三12、 在类的成员函数中能够访问同类型实例的私有变量

类成员变量的访问权限是编译器强加的,只要编译器能够找到符号,就能够经过编译。

在类中的同类实例的私有变量符号能够在当前类域中查找到,因此能够经过编译;但在类外,由于没法访问类的私有变量,没法找到符号。

三十3、 初始化二维数组

1.A (*ga)[n] = new A[m][n];
...
delete []ga;
缺点:n必须是已知
优势:调用直观,连续储存,程序简洁(通过测试,析构函数能正确调用)

2. A** ga = new A*[m];
for(int i = 0; i < m; i++)
ga[i] = new A[n];
...
for(int i = 0; i < m; i++)
delete []ga[i];
delete []ga;
缺点:非连续储存,程序烦琐,ga为A**类型
优势:调用直观,n能够不是已知

3. A* ga = new A[m*n];
...
delete []ga;
缺点:调用不够直观
优势:连续储存,n能够不是已知

4. vector<vector<A> > ga;
ga.resize(m); //这三行可用可不用
for(int i = 1; i < n; i++) //
ga[i].resize(n); //
...

缺点:非连续储存,调试不够方便,编译速度降低,程序膨胀(实际速度差异不大)
优势:调用直观,自动析构与释放内存,能够调用stl相关函数,动态增加

5. vector<vector<A> > ga;
ga.resize(m*n);
方法3,4的结合

6. 2的改进版
A** ga = new A*[m];
ga[0] = new A[m*n];
for(int i = 1; i < m; i++)
ga[i] = ga[i-1]+n;
优势:连续存储,n能够不是已知,析构方便,猜测只需delete [] ga;

三十4、 格式化输出

#include <iomanip>
cout << setfill('*') << setw(6) << count << endl;

三十5、 随机数

C中的随机数:

#include <stdlib.h>
a = rand() % 100;       //生成0到100的随机数
a = srand((unsigned) time(NULL));   //使用系统当前时间做为种子点

C++中的随机数:

//随机数引擎
default_random_engine e;
for (size_t i = 0; i < 10; ++i)
    cout << e() << " ";

标准库定义了多个随机数引擎类,区别在于性能和随机性质量不一样。引擎的操做以下:

Engine e;
Engine e(s);        //使用整型值s做为种子点
e.seed(s);          //从新设置种子为s
e.min() e.max()     //引擎生成的最小值和最大值

为了获得一个指定范围内的数,咱们使用一个分布类型的对象:

uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e;
for (size_t i = 0; i < 10; ++i)
    cout << u(e) << " ";        //生成均匀分布的unsigned值

3六、内存操做

#include <string.h>
memcpy(dst, src, size);
memset(src, 0, size);
int* a = (int*)malloc(size);
if (a)
    free(a);

37. c string

#include <string.h>
char* str = "hello world";
int len = strlen(str);
strcmp(str1, str2);
strcpy(str1, str2);
strncpy(str1, str2, size); //拷贝前size个字符
strcat(str1, str2);
strncat(str1, str2, size);   //链接前size个字符
char *strchr(const char *s,char c);         //查找字符串s中首次出现字符c的位置
strstr(string,search);              //查找字符串s中出现字符串search的位置

38. clock vs time

clock是cpu用时,time是从UTC时间开始计时,程序真正执行的时间。

// first
#include <time.h>
clock_t startTime = clock();
int elapsedTime = (clock() - startTime) / CLOCKS_PER_SEC;

// second
time_t a, b;
a = time(NULL);
//...
b = time(NULL);
float elapsed = b - a;

39. shared_ptr

gcc中的头文件

//vs2010使用#include <memory>, 
//c++11中也直接#include<memory>,已将将它加入标准空间std中了,不须要使用tr1了

#include <tr1/memory>       


using std::tr1::shared_ptr;

void foo(){
    // c++11嵌套<>已经能够区分,不用加空格,但为了保证兼容性,最好仍是加上空格,由于c++11目前使用的还比较少
    shared_ptr<Matrix<int> > ptrm(new Matrix<int>(3, 4));
    ptrm->print();
}

shared_ptr<Widget> s1(new Widget());
cout << s1.use_count() << endl;    //当前有1个reference
shared_ptr<Widget> s2 = s1;
cout << s1.use_count() << endl;     //当前有2个reference
s1->print();                         //调用Widget类内函数
s1.reset();                         
cout << s2.use_cout() << endl;      //当前有1个reference
s2.reset();                         //会调用Widget的析构函数

40. C++类型转换

  1. const_cast:常量转很是量
  2. static_cast: 最经常使用,将intdouble
  3. dynamic_cast:安全向下转型,转换为子类,效率低
  4. interpret_cast:转换一个指针为其它类型的指针。它也容许从一个指针转换为整数类型。反之亦然。(译注:是指针具体的地址值做为整数值?)这个操做符可以在非相关的类型之间转换。操做结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不作任何类型的检查和转换。

reinterpret_cast:

class A {};
class B {};

A * a = new A;
B * b = reinterpret_cast<B *>(a);

reinterpret_cast就像传统的类型转换同样对待全部指针的类型转换。

dynamic_cast:

class A
{
    public:
        int m_iNum;
        virtual void f(){}
};

class B:public A
{
};

class D:public A
{
};
void foo()
{
    B* pb = new B;
    pb->m_iNum = 100;
    D* pd1 = static_cast<D*>(pb); //compile error
    D* pd2 = dynamic_cast<D*>(pb); //pd2 is NULL
    delete pb;
}

在函数foo中,使用static_cast进行转换是不被容许的,将在编译时出错,而使用 dynamic_cast的转换则是容许的,结果是空指针。dynamic_cast只有当pb真的指向class D的实例时才会有效。

41. 数值

2^16 = 65536
2^20 = 1048576

42. const

  1. const 做用域

    const char * p = "hello";       //p所指向的字符串不能变
     char const * p = "hello";       //同上效果
     char* const p = "hello";        //p的值没法改变,不能指向它处
  2. const 重载

(1) const 修饰参数
+ 若参数是基本类型,不可构成重载
+ 若参数是自定义类型,能够构成重载

(2) const 修饰函数
能够构成重载,const成员函数也不能在函数中调用其余非const 的函数

(3) const 修饰返回值
不能够进行重载

  1. const的例外

    const函数执行bitwise检查,函数里不能够改变值。若要进行改变,须要将变量声明为mutable,添加例外。

  2. const的调用

    对象.成员函数
          对象          成员函数         对/错
     一、  const         const            对
     二、  const         non-const        错 
     三、  non-const     const            对
     四、  non-const     non-const        对
    
     成员函数调用成员函数
          成员函数       成员函数         对/错
     五、  const         const            对
     六、  const         non-const        错
     七、  non-const     const            对
     八、  non-const     non-const        对

43. 线程安全

mutex:互斥
实现:

44. volatile

它是被设计用来修饰被不一样线程访问和修改的变量。若是没有volatile,基本上会致使这样的结果:要么没法编写多线程程序,要么编译器失去大量优化的机会。volatile修饰的变量可能随时发生变化,每次都要从新读取,编译器不要进行优化。

volatile的做用是: 做为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.

45. binary_function

template <typename T>
struct com : public binary_function<Matrix<T>, Matrix<T>, bool>{
    bool operator() (const Matrix<T>& lhs, const Matrix<T>& rhs) const{
       return lhs.getD() < rhs.getD(); 
    }
};

//排序算法中调用
sort(v.begin(), v.end(), com<int>());
//或者先定义一个对象,再传进去
com<int> in;
sort(v.begin(), v.end(), in);

46. 常量区

char str1[] = "abc";
char str2[] = "abc";
const char3[] = "abc";
const char4[] = "abc";
char *str5 = "abc";
char *str6 = "abc";
const char *str7 = "abc";
const char *str8 = "abc";

其中,str1, str2, str3, str4分别指向不一样的地址,后面的四个指向共同的常量区。而且常量字符没法修改,尝试修改会出现Segment Fault

47. 进制

0x数据 十六进制
0数据 八进制
直接数据 十进制

而%d,%o,%x,%X 分别是 十进制,八进制,十六进制(小写),十六进制(大些)的输出格式!

48. extern "C"

C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不一样。假设某个函数的原型为: void foo(int x, int y);
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C链接交换指定符号extern“C”来解决名字匹配问题。

49. strcpy

(1)strcpy实现

char *strcpy(char *strDest, const char *strSrc);
{
    assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
    char *address = strDest;    // 2分
    while( (*strDest++ = * strSrc++) != ‘\0’ )  // 2分
       NULL ; 
    return address ;    // 2分
}

(2)strcpy能把strSrc的内容复制到strDest,为何还要char * 类型的返回值?
答:为了实现链式表达式。 // 2分

int length = strlen( strcpy( strDest, “hello world”) );

50. 复杂声明

右左法则:首先从最里面的圆括号看起,而后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面全部的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

  1. 先从未定义的标识符func开始,因为左边是*,说明func是个指针,跳出括号,看右边是个括号,func是个函数指针,指向的函数有int*的参数,返回值为int.

    int (*func)(int *p);
  2. func开始,左侧是*,说明是指针,跳出括号,右侧是个括号,说明func是个函数指针,其函数是两个参数,一个是int*,另外一个是以int*为参数,int为返回值的函数指针,外面函数返回值类型是int.

    int (*func)(int *p, int (*f)(int*));
  3. 先看func,[]的优先级大于,先看左侧[],func是一个数组,再看*,这个数组存放的是指针,跳出括号,看右侧是个括号,可知数组中存放的是函数指针,其函数有一个int的参数,返回值为int.

    int (*func[5])(int *p);
  4. 先看func,其左侧是个*,可知是个指针,跳出括号,左侧是个数组,说明func是个指向数组的指针,右侧有个 *,说明func中存放的是函数指针,其函数有个参数是int*,返回值是int。

    int (*(*func)[5])(int *p);
  5. func是个函数指针,参数是int*,返回值是指向数组的指针,指向的数组有5个int

    int (*(*func)(int *p))[5];
  6. 也有一些不符合语法规则:不返回数组;不容许函数数组,由于通常函数大小不同。

    int func(void) [5];
    
     int func[5](void);
  7. 其它

    int (*(*func)[5][6])[7][8];
    
     int (*(*(*func)(int *))[5])(int *);
    
     int (*(*func[7][8][9])(int*))[5];
  8. 对表达式分解会更加清晰

    int (*(*func)(int *p))[5];

    能够这样分解:

    typedef int (*PARA)[5]; 
     typedef PARA (*func)(int *);

51. typename vs class

  1. typenameclass均可以用来在模板中声明模板参数和类型参数。早期只有class,后来为了区分类,提供了typename,在这一方面,typenameclass同样,根据习惯使用。

  2. typename还有一个用途,标明内嵌类型名,这时只能使用typename

    typename vecotr::const_iterator start(v.begin());           //声明内嵌类型const_iterator

52. C++ 三个基本特征

封装:隐藏实现细节,使代码模块化。把客观事物封装成抽象的类,而且类能够把本身的数据和方法只让可信的类或者对象操做,对不可信的进行隐藏。(代码重用)

继承:继承可使用现有类的全部功能,无需从新编写原来的类而对其进行扩展。(代码重用)

多态:父对象就能够根据当前赋值给它的子对象的特性以不一样的方式运做,容许将子类类型的指针赋值给父类类型的指针。

53. 继承方式

  1. 继承方式

    public继承: public => public, protect =>protect, private => 不可访问
    protect继承:public => protect, protect =>protect, private => 不可访问
    private继承:public => private, protect => private, private => 不可访问

  2. 子类成员

    public成员:public => public, protect => protect, private => private
    protect成员:public => protect, protect => protect, private => private
    private成员:都不可访问

默认继承方式是private继承,默认成员是private。

54. ,表达式

逗号表达式的通常形式是:表达式1,表达式2,表达式3……表达式n
逗号表达式的求解过程是:先计算表达式1的值,再计算表达式2的值,……一直计算到表达式n的值。最后整个逗号表达式的值是表达式n的值。

int x = 7, y = 2, z;
z = (x%y,x/y)  先计算x%y =1, 再计算x/y=3,而后 z=(1,3)=3

55. 栈展开

因发生异常而逐步退出复合语句和函数定义的过程,被称为栈展开。

栈展开过程沿着嵌套函数的调用链不断查找,直到找到与异常匹配的catch子句为止,若是没有找到匹配的子句,调用标准库中的terminate函数终止进程

在栈展开过程,在一些语句块建立的对象应该被正确销毁,调用其析构函数。

56. 异常

  1. catch子句的参数类型

    使用pass by reference方法,能够对异常的修改起做用而且提升效率,通常将catch的参数类型定义为引用类型。

  2. 查找匹配的catch子句

    a. 类型转换支持的比函数参数匹配的少,不支持算术类型转换和类类型转换,只支持以下:
    • 很是量向常量转型
    • 派生类向基类转型
    • 数组被转换成指向数组类型的指针

    b. 匹配顺序
    函数匹配是best-fit进行的,选择匹配参数最好的,而异常问题是按first fit进行,按照顺序进行,因此要把子类(及特殊类型)放在通用类型的前面。

  3. 异常抛出

    异常抛出:

    Exception ex;
     throw ex;

    异常抛出时,不论catch是以什么方式捕获by referenceby value都须要进行复制,到catch子句上的正是这个异常的副本。

    异常从新抛出:

    catch (exception& ex)
     {
         //....
         throw;                  //从新抛出此exception,使它继续传播
     }   
     catch (exception& ex)
     {
         //....
         throw ex;               //传播被捕捉的exception的一个副本
     }

57. VS2010的配置选项

  1. 若是用到dll文件,能够添加一个后期生成事件,拷贝dll文件到Debug目录:

    copy D:\app\vlfeat-0.9.9\bin\w32\vl.dll $(SolutionDir)$(ConfigurationName)

58. 内联函数

  1. 在内联函数内不要使用循环语句和开关语句,这样就达不到内联的效果了。
  2. 关键字inline必须与函数定义体放在一块儿才能使函数成为内联,仅将inline 放在函数声明前面不起任何做用

59. memset

memset的用法以下:

const int NUM = 100;
int* src = new int[NUM];
memset(src, 0, sizeof(int)*NUM)

memset是按字节赋值的,不能将int赋1,若是赋1,实际结果将是0x01010101,与咱们的预期不一样。

60. virtual 析构函数

当子类对象经由父类的指针删除时,若是是non-virtual函数,将不会调用子类的析构函数,也就是说,对象的子类成分有可能没被销毁。

61. ifstream

  1. good() vs eof()

    good()表示文件流是否正常,eof表示文件流是否到结束了。good() 等价于 !(bad() || eof() || failed())

  2. ifstream in(string)

    有些编译器不支持以string做为参数,为了兼容性,最后仍是转为c串。

62. strtok

char buf[]=”Golden Global View”;
char* token = strtok( buf, " ");
while( token != NULL )
{
    printf( ”%s “, token );
    token = strtok( NULL, ” “);
}

63. ::双冒号

//双冒号也经常用于在类变量内部做为当前域的元素进行表示,好比:    
int CA::add(int a)    
{    
    return a + ::ca_var;    
}
//表示当前类实例中的变量ca_var。
//若是当前哉没有,则向上一级域查找,直到找到全局域

64. 区别位运算与关系运算

位运算只有一个字符:&, |, ^
关系运算有两个字符:&&, ||

65. 静态分配,动态分配

内存的静态分配和动态分配的区别主要是两个:

  1. 一是时间不一样。静态分配发生在程序编译和链接的时候。动态分配则发生在程序调入和执行的时候。
  2. 二是空间不一样。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,好比局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不一样,他的动态分配是由编译器进行释放,无需咱们手工实现。

堆和栈均可以动态分配,但堆必须动态分配,不能静态分配。

66. 笔试题

  1. 字符串大小

    char dog[] = "wang\0miao";
     sizeof(dog)     //大小为10
     strlen(dog)     //大小为4
  2. 系统位数对类型大小的影响

    联合体,结构体的总大小为结构体最宽基本类型成员大小的整数倍。

    32位: int 4; 指针 4;
     64位: int 4; 指针 8;

    64位机器:

    /*******size of types on centos_x32*****************/
     size of char:1
     size of int:4
     size of long :4
     size of float :4
     size of long long:8
     size of double:8
     size of long double:12
     size of char * :4
     /*******size of types on centos_x64*****************/
     size of char:1
     size of int:4
     size of long :8
     size of float :4
     size of long long:8
     size of double:8
     size of long double:16
     size of char * :8
    
              32位        64位 
     char      1           1 
     int       4           大多数4,少数8 
     long      4           8 
     float     4           4 
     double    8           8 
     指针       4           8
  3. 字符串初始化方法

    char a[] = "ABC";          //大小为4,最后有\0
     char b[] = {"A", "B", "C"};  //大小为3, 字符数组
  4. 数组指针和指针数组

    int* a[10];        //指针数组
     int (*b)[10];      //数组指针,指向有10个char组成的数组的指针
     sizeof(a);         //sizeof(int*) * 10 = 4 * 10 = 40(32位机器)
     sizeof(b);         //sizeof(指针)=4 (32位机器)
  5. 位域

    C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不一样的区域, 并说明每一个区域的位数。每一个域有一个域名,容许在程序中按域名进行操做。 这样就能够把几个不一样的对象用一个字节的二进制位域来表示。

    struct A{
         int x : 3;
         int y : 4;
         int z : 5;     //占用int中的5位 
     };
    
     sizeof(A);    //大小为4字节,由于只占用了一个int
  6. 纯虚函数

    纯虚函数能够有函数体,但没有什么意义。

  7. 类型转换

    32位机上根据下面的代码,问哪些说法是正确的?()
     signed char a = 0xe0;
     unsigned int b = a;
     unsigned char c = a;
    
     A. a>0 && c>0 为真
     B. a == c 为真
     C. b 的十六进制表示是:0xffffffe0
     D. 上面都不对
    1. 同等位数的类型之间的赋值表达式不会改变其在内存之中的表现形式,所以经过 unsigned char c = a;语句,c的位存储形式仍是0xe0
    2. B选项中的"==" 左右两边须要进行Integer Promotion,将其提高为int类型。
    3. 提高的方法:若是原始类型为unsigned ,进行零扩展;若是原始类型为signed,进行符号位扩展,注意是原来的类型,不是提高后的类型。
    4. 若是unsigned和int进行计算,则会将int转化成unsigned以后进行计算,但不会改变内存的表现形式。

    好比下面的代码,返回的结果为真,由于a和b进行转型时,不发动内存内容,而==比较都会转换为int,因此最后结果相同。

    signed int a = 0xe0000000
     unsigned int b = a;
     cout<< (b == a) <<endl;
  8. ASCII表

    Alt text

    0x20:  space
     0x30:  0
     0x41:  A
     0x61:  a
  9. 汇编

    AT&T风格的汇编代码。

    int main()
     {
         char data = 'a';
         char* p = &data;
         *p++;
         (*p)++;
         return 0;
     }
    
     //使用objdump -S main 输出mian部分的汇编代码
    
     0000000000400746 <main>:
       400746:   55                      push   %rbp
       400747:   48 89 e5                mov    %rsp,%rbp
       40074a:   48 83 ec 20             sub    $0x20,%rsp
       40074e:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
       400755:   00 00 
       400757:   48 89 45 f8             mov    %rax,-0x8(%rbp)
       40075b:   31 c0                   xor    %eax,%eax
    
     l1:    char data = 'a';    
       40075d:   c6 45 ef 61             movb   $0x61,-0x11(%rbp)   //rbp[-0x11] = 0x61
    
     l2:    char* p = &data;
       400761:   48 8d 45 ef             lea    -0x11(%rbp),%rax
       400765:   48 89 45 f0             mov    %rax,-0x10(%rbp)
    
     l3:     *p++;
       400769:   48 83 45 f0 01          addq   $0x1,-0x10(%rbp)
    
     l4:    (*p)++;
       40076e:   48 8b 45 f0             mov    -0x10(%rbp),%rax
       400772:   0f b6 00                movzbl (%rax),%eax
       400775:   83 c0 01                add    $0x1,%eax
       400778:   89 c2                   mov    %eax,%edx
       40077a:   48 8b 45 f0             mov    -0x10(%rbp),%rax
    
       40077e:   88 10                   mov    %dl,(%rax)
       400780:   b8 00 00 00 00          mov    $0x0,%eax
       400785:   48 8b 4d f8             mov    -0x8(%rbp),%rcx
       400789:   64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
       400790:   00 00 
       400792:   74 05                   je     400799 <main+0x53>
       400794:   e8 a7 fe ff ff          callq  400640 <__stack_chk_fail@plt>
       400799:   c9                      leaveq 
       40079a:   c3                      retq
  10. 数组名

    数组名是常量,没法给值赋值。

    char str[100];
    str++;    //出错,str是常量没法修改
  11. 类方法是指类中被static修饰的方法,无this指针。

相关文章
相关标签/搜索