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两次,既不能释放了再释放
-
- 当明确知道数值不可能为负时,选用无符号类型。
- 在算术表达式中不要使用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,
和空类对象是一样的,所以可以得出,成员函数是不占空间的。