c++小知识

  • 未初始化的全局变量,程序启动时自动将其全部初始化为0(即变量的每个比特都是0)。
  • 未初始化的局部变量,初始值是随机的。
  • “构造函数”并不负责为对象分配内存空间,构造函数执行时,对象的内存空间已经分配好了,构造函数的作用是初始化这片空间。
  • 使用sizeof运算符技术对象占用的存储空间时,不会将静态成员变量计算在内。      
#include<iostream>
using namespace std;
class Demo{
 int a,b;
 static int c,d;
};
int main(){cout<<sizeof(Demo);}

输出结果:

8
  • 面向对象的程序设计有“抽象”、“封装”、“继承”、“多态”四个基本点。
  • 面向过程的程序设计:数据结构+算法=程序
  • 面向对象的程序设计:类+类+类+······+类=程序
  • 构造函数不能以本类的对象作为唯一参数,以免和复制构造函数想混淆。例如,不能写如下构造函数 

 

class A{
   A(A a){······}
};
  • 以对象作为函数的形参,在函数被调用时,生成的形参要用复制构造函数初始化,这会带来时间上的开销。如果用对象的引用而不是对象作为形参,就没有这个问题了。但是以引用作用形参有一定的风险,因为这种情况下如果形参的值发生改变,实参的值也会跟着改变。如果要确保实参的值不会改变,有希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的const引用。例如:
void f(const A&a){}
  • int a[10] 数组地址a 、&a 是一样的,但是 a+1 、&a+1 是不同的,因为a是 int*类型   &a是 int(*)[10] 类型   所以  a+1相当于 a[1]  &a+1相当于 a[10];
#include<iostream>
using namespace std;
void f(int * a) {}
int main() {
	int a[10];
	cout << *a << endl <<
		a << endl <<
		&a << endl <<
		(a + 1) << endl <<
		(*a + 1) << endl <<
		(&a + 1) << endl <<
		*(&a + 1) << endl;
	    //f((&a))

}

  

 输出结果:

-858993460
0136FE90
0136FE90
0136FE94
-858993459
0136FEB8
0136FEB8

                         示例图片:             

 

  • 重载运算符“()”、"[]"、"->"、或者赋值运算符"="时,只能将它们重载为成员函数,不能重载为全局函数。
  • 判断好函数的返回值是 bool 或者其他类型。或者string 为“”空。
  •  区分多态和非多态:通过基类指针或引用调用成员函数的语句,只有当该成员函数是虚函数时才会是多态。如果该成员函数不是虚函数,那么这条函数调用语句就是静态联编的,编译时就能确定调用的是哪个类的成员函数
  • c++规定,只要基类中的某个函数被声明为虚函数,则派生类中的同名、同参数表的成员函数即使前面不写virtual关键字,也自动成为虚函数。
  • 多态是通过虚函数表实现的。
  • 在普通成员函数中调用虚函数是多态,但在构造函数和析构函数中调用虚函数不是多态。
  • 有虚函数的类,其析构函数也应该实现为虚函数。
  • 包含纯虚函数的类叫抽象类。不能用抽象类定义对象。抽象类的派生类,仅当实现了所有的纯虚函数,才会变成非抽象类。
  • 用new运算符动态生成的对象都是通过释放指向它的指针来释放的。如果一个基类指针指向用new运算符动态生成的派生类对象,而释放该对象时是通过释放该基类指针来完成的,那么执行的就是基类的析构函数。所以才有了虚析构函数。
  • 每一个函数存储在内存里,因此拥有一个地址,在c++中,指向函数的指针是合法的数据值。
  • list可以有一个所谓的头节点,这个头节点是链表的开始,它指向链表的第一个元素,本身不存储任何东西
    http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=4193458
    头节点不存放数据可以令一些链表操作更方便,比如删除连表中某个节点x: 假设头节点不存放数据: p = head; q = head->next; while (q) { if (q == x) { p->next = q->next; delete x; break; } p = q; q = q->next; } return; 如果头节点存放数据,那删除节点的操作需要单独处理头节点: if (x == head) { head = head->next; delete x; return; } p = head; q = head->next; while (q) { if (q == x) { p->next = q->next; delete x; break; } p = q; q = q->next; } return;

      有这个空的头节点, 只是为了避免:
    1. 插入节点, 每次都要额外判断一下"链表是不是空".
    2. 删除节点, 每次都要额外判断一下"是不是删了头节点".

  • cout<<"xxx";
    

      "xxx"是一个字符串地址 与 char a[3]="yyy"; 的a等同。若是在”xxx“后面加字符 即

    cout<<"xxx"+'*';
    

      此时相当于在”xxx“这个地址上偏移42个字节(*的ASCII码为42)后输出。例如

    #include<iostream>
    using namespace std; 
    
    int main()
    {
    	for(int i=0;i<100;i++)
    	cout <<" "+i;
    	return 0;
    }
    

      输出为

  •  

    static 类成员函数 只要在声明处有 static声明即可,定义不需要static

  • 结构体struct 重载运算符<<

    在结构体内定义  friend ostream & operator<< (ostream &os,const Info info);
    在结构体外定义   ostream & operator<< (ostream &os,const Info info);

      

  • c++一直可以delete空指针
  • 同一个指针不能delete两次,既不能释放了再释放
  •  

     

    c++不能直接delete的是野指针,会出问题的,所以一般指针被delete之后,最好立即赋值为NULL,以免被再次delete而出现问题。
  • 当明确知道数值不可能为负时,选用无符号类型。
  • 在算术表达式中不要使用char或bool,char在一些机器是有符号的,在另一些机器又是无符号的,所以如果要用char类型,要明确指定是signed char 还是unsigned char。
  • 执行浮点数运算选用double,因为double和float的计算代价相差无几。事实上,对于某些机器,double会比float更快。而long double提供的精度一般是无必要的,且运行时的消耗不容忽视
  • 使用{} (列表初始化)进行初始化且初始值存在丢失信息的危险,则编译器将报错。

     

	double a = 123.123;
	int b { a };
or
	double a = 123.123;
	int b ={ a };
int b=(a);
int b(a);则没问题
  • 头文件不应包含using声明,因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明。对于某些程序来说,由于不经意间包含了一些名字,反而可能会产生始料未及的名字冲突
  • vector <int>::size_type;//对
    vector::size_type;//错
    

      

  • vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素
  • 迭代器使用递增(++)运算符来从一个元素移动到下一个元素。但因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用的操作。 
  • 凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
  • 对数组进行初始化时,会用提供的初始值初始化靠前的元素,剩下的元素被初始化成默认值。
  • 在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
  • string和vector也能执行下标运算,但是数组与它们相比还是有所不同。标准库类型限定使用的下标必须是无符号类型,而内置的下标运算无此要求(内置的下标运算符的索引值不是无符号类型),可以处理负值。
  • 	int a = 5;
    	if (a == true)
    		cout << "s";  
    

      因为a不是布尔值,所以进行比较前会首先把true转换成val的类型 在这里会转换成1。所以 if为false

  • 如果程序崩溃,输出缓冲区不会被刷新。即可能代码已经执行了,但输出数据被挂起没有打印唯而已。
  • 当一个fstream对象被销毁时,close会自动被调用。同一个i/of/stream,close后才能重新open另一个文件。
  • 保留被ofstream打开的文件中已有数据的唯一方法是显示指定app或in模式。
  • ifstream关联的文件,默认以 in 模式打开,ofstream 以out  fstream 以in和out。
  • 向一个vecotr、string或deque插入元素会使所以指向容器的迭代器、引用和指针失效,插入到任何位置是合法的,但可能很耗时。
  • 遍历且插入的时候不能用vector<int>::iterator,否则可能因为vector重新allocate内存而地址失效,所以直接使用数组下标。
  • STL 中的vector 以及其他容器 都是 非线程安全的,使用 iterator 迭代器 ,和 插入元素 不进行 加锁控制,会导致严重的问题。
  • vector 的insert 是返回插入的地址
  • 容器元素是拷贝,即非引用参数。
  • 标准库算法(泛型算法)操作的是迭代器而不是对容器进行操作。
  • new出来的动态数组并不是数组类型。
  • char ch;//灾难
    while((ch=cin.get())!=EOF);//从cin.get.()返回的值被转换为char,然后与一个int比较
        cout.put(ch);
    

      不要这样写,c++ primer 中文第五版p675

  • set中判断元素是否相等: 
    if(!(A<B || B<A)),当A<B和B<A都为假时,它们相等。

  • int a[8][5]  跟  int** a 不是同一回事  作为形参时要注意
  • float a[2][2]={0,1,2,3};
    float **p=(float**)a;//强制将二维数组指针转为指向指针的指针

    则此时

    p[0]=0;
    p[1]=1;
    p[2]=2;
    p[3]=3;

    p[0][0]=*(*(p+0)+0)=**p;
    p[0][1]=*(*(p+0)+1);

    p[0][0]  由于*p=0; ====> **p=*(0);  引用地址为零的内存,必然是错误的。

    p[0][1]=*(*p+1)====>*(4),  引用了非法内存

  • fstream 如果没有使用ios::binary标志位,然后进行write/read的话  0a 会变成 0d0a。换行符
  • 	string a = "sadas";
    	const char* p = a.c_str();	
    	cout << p << endl;
    	a = "zxczx";
    	cout << p << endl;
    

      输出是

    sadas
    zxczx
    

      小心string 的c_str()

  • 在debug下编译通过后再转去release。sort 慎用
  • vector resize 是预分配空间,并初始化。后面就能直接用a[100]=10;  reserve不行,会报越界 
  •  sizeof是编译期展开的 ,所以可以得到静态数组的大小,编译期得到静态数组大小 
  • 例2 . 17 中类CB ox 中只有一个成员函数,类CB ox 的对象boxobj 的大小却只有l Byte,
    和空类对象是一样的,所以可以得出,成员函数是不占空间的。

     

posted @ 2017-10-29 00:13  ff_d  阅读(220)  评论(0编辑  收藏  举报