C++之乱七八糟<真正的随笔>

1、new和delete

int* pi = new int(0);	//把值初始化为0
A* pA = new A();	       //对于自定义类型,创建对象分两步,第一步分配内存,第二步调用构造函数 A()是构造函数。
pA->function();delete pA;	 //对于自定义类型,第一步调用析构函数,第二步释放内存。
int *pi = new int[10];delete []pi;    //申请10个元素的一维数组
int **pi = new int[5][6];
delete [][]pi;//申请一个二维数组。
const int *pci = new const int(1024);

动态创建的 const 对象必须在创建时初始化,并且一经初始化,其值就不能再修改。delete p后,该指针变成悬垂指针,有可能指向内存的任何一个位置。一旦删除了指针所指向的对象,立即将指针置为 0,即指向NULL,这样就非常清楚地表明指针不再指向任何对象

2、malloc 和 free
动态内存管理容易出错
1、删除( delete )指向动态分配内存的指针失败,因而无法将该块内存返还给自由存储区。删除动态分配内存失败称为“内存泄漏(memory leak)”。内存泄漏很难发现,一般需等应用程序运行了一段时间后,耗尽了所有内存空间时,内存泄漏才会显露出来。   delete后要检测一下。
2、读写已删除的对象。如果删除指针所指向的对象之后,将指针置为 0 值,则比较容易检测出这类错误。
3、对同一个内存空间使用两次 delete 表达式。当两个指针指向同一个动态创建的对象,删除时就会发生错误。如果在其中一个指针上做 delete 运算,将该对象的内存空间返还给自由存储区,然后接着 delete 第二个指针,此时则自由存储区可能会被破坏。

3、const用法

1、定义后左值就不能被修改,所以定义时必须初始化。
const对象默认为局部变量,若想全局使用,必须显示声明extern。
文件1  extern const int buffersize=512;  //定义
文件2  extern const int buffersize;      //使用
2、const 引用是指向 const 对象的引用 

const int ival = 1024;const int &refVal = ival; 
int &ref2 = ival;   //n 非_常引用 指向指向了一个常对象
const 引用可以初始化为不同类型的对象或者初始化为右值
int i = 42;         
const int &r = 42;      //  legal for const references only
const int &r2 = r + i;    

double dval = 3.14;
const int &ri = dval;
 
//编译器会把这些代码转换成如以下形式的编码:   
int temp = dval;          // create temporary int from the double
const int &ri = temp;     // bind ri to that temporary 

如果 ri 不是 const,那么可以给 ri 赋一新值。这样做不会修改 dval,而是修改了 temp。期望对 ri 的赋值会修改 dval 的程序员会发现 dval 并没有被修改。仅允许 const 引用绑定到需要临时使用的值完全避免了这个问题,因为 const 引用是只读的。
3、常指针,指针常量

const int* p1 = &i;//常量指针,指向一个常量的指针,等同于int const* p1 = &i;
//*p1 = 2;         //指针指向的内容不能改
p1 = &j;           //指针可以修改,可以指向别的变量
int* const p2 = &i;//指针常量,不能修改p2。必须初始化。一个指针,是常量
*p2 = 2;           //指针指向的内容可以修改,
//p2 = &j;         //指针不能指向别的变量
const int* const p3 = &i;//指向常量的常指针
const int& ri = i;//常量引用,不能修改ri的值。                        
//ri = 10;    
i = 10;       OK
 

4、常函数
//写一个类的时候,尽可能多的声明常函数。以满足常量对象的调用需求。
int GetN() const   //常函数,在函数内不能修改类的数据成员。
            //编译器解释为 int GetN(const A* this)
                          //必须是类的成员函数常对象只能调用常函数。
const修饰形参,提高参数传递的效率,保证传入参数不被修改!一般不要返回数据成员的地址或者引用,以免破坏类的封装性。
const string& GetS() const {return s;}     //用const修饰返回值

4、static
    int i;                 //实例成员,属于对象,每个对象都有自己的一份拷贝,只能通过对象访问
    static int j;        //静态成员,属于类,所有对象共享一份数据。可以通过对象名或者类名访问。
    int A::k = 0;     //静态成员需要在类外初始化
    static void function();   //静态成员函数:无this指针,不能访问类的实例成员,只能访问类的静态成员。

5、初始化
   int ival(1024);     // 直接初始化则是把初始化式放在括号中【更灵活效率更高】
   int ival = 1024;    // 复制初始化语法用等号(=)
    初始化列表
    A::A() : i(2),j(3),k(10){......}A::A(int a,int b):i(a),j(b){}   //A的私有数据成员i,j 

6、类

1.构造函数Student 的私有成员m_ID,m_cName

Student::Student(char *pName, int ssId)
{
	m_ID = ssId;
	strcpy(m_cName, pName);
	cout << "construct new student" <<endl;
}

2.拷贝构造函数  

Student::Student(const Student& Stu)
{
	m_ID = Stu.m_ID;
	strcpy(m_cName, Stu.m_cName);
	cout << "construct copy of" << Stu.m_cName << endl;
}

3.浅拷贝  自己不定义,C++默认的拷贝构造函数深拷贝  
Student::Student(Student& stu)
{
	cout <<"Constructing " <<stu.pName <<endl;
	pName=new char[strlen(stu.pName)+1];
	if(pName!=0)
	{
		strcpy(pName, stu.pName);
	}
}

7、域名空间
namespace N1{void F(){}}   //自定义
useing namespace N1;    F();  /调用
或者N1:: F();

8、函数参数缺省
int add ( int x,int y = 5,int z = 4 );  Y   
int add(int x = 3,int y,int z);   N
缺省参数(default value)由后向前

9、传递指向指针的引用
void ptrswap(int *&v1, int *&v2)  //int *&v1理解为v1是一个引用,与指向int型对象的指针相关联。
{ 
    int *tmp = v2;                 //也就是说,v1 只是传递进 ptrswap 函数的任意指针的别名。
    v2 = v1;
    v1 = tmp;   
}         //使用的时候用指针ptrswap(pi1,pi2)

10、头文件
1、头文件用于声明而不是用于定义
2、定义const 对象
3、inline函数
11、静态联编与动态联编 

calss A{public:void f(){cout<<"a";}};
class B:public A{public:void f(){cout<<"b";}};
class C:public A{public:void f(){cout<<"c";}};
class D : public A{};   //没有重写,继承父类的函数,也是抽象类,不能实例化。(该例子是动态联编的)
void Test(A& ra){
ra.f();//取决于调用f1的引用或者指针的声明类型。叫做静态联编,也就是编译时已经确定了会执行哪个函数体。
}
void main(){
	B b;
	A& ra = b;	
	ra.f();        //输出a
	A* pa = &b;
	pa->f();      //输出a
	Test (b);     //输出a     //如果A类的前面加上virtual ,则变成动态联编,这时输出b;
}      

12、动态联编条件:1.用基类的引用(指针)指向派生类的对象 2.只有调用虚函数,才有动态联编的效果。 

class A                    // 抽象类:含有纯虚函数的类。不能实例化。
{
public :virtual void f1() {cout << "A f1" << endl;}	//父类中声明纯虚函数
};
如果一个类有可能作为基类,那么需要把它的析构函数写成虚函数。派生类的析构函数自动成为虚函数

13、指针
1、指针使用前要初始化!!!!
---->运行时的错误如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的位内容。使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据。因此,在对未初始化的指针进行解引用时,通常会导致程序崩溃。
2、特殊的指针类型 void*,它可以保存任何类型对象的地址
3、指向指针的指针

//定义:
 int *pi = &i;
 int **ppi =π
使用:解引用2次
cout<<**ppi<<endl;
4、指针和数组:
int *ip = ia;       // ip 指向ia[0] ia是一个数组名。
int *ip2 = ip + 4;  // ok: 指针+4,则直至后移4个单位,然后赋值给指针ip2,注意不要越界
指针还支持减操作
int last = *(ia + 4)   //取数组的第四个元素,解引用后赋值给last
int last = *ia + 4     //取数组第一个元素,解引用后+4

指针和下标
int *p = &ia[2];     // ok: p 指向数组ia的第2个元素
int j = p[1];        // ok: p[1] 相当于 *(p + 1),即ia[3]
int k = p[-2];       // ok: p[-2] 相当于ia[0]
指针遍历数组    -------指针相当于数组的迭代器
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };//pbegin指向数组第一个元素,pend指向最后一个元素
for (int *pbegin = int_arr, *pend = int_arr + arr_sz;pbegin != pend; ++pbegin)
      cout << *pbegin << ' '; // 输出当前对象
14、位操作符    
unsigned char               bit1 = 0227      //10010111   
~  bitwise NOT(位求反)    bit2=~bit1;      //01101000 
<<  left shift(左移)      bit1<<1          //00110110  整体左移,右侧补0
>>  right shift(右移)     bit1>>2          //00100101  整体右移,左侧补0
&  bitwise AND(位与)      和bit逐位取与操作
^  bitwise XOR(位异或)    和bit逐位取异或操作
|  bitwise OR(位或)       和bit逐位取或操作

15、自增自减运算符 
后置++  a++;

cout<<*p++<<endl;  等价于  cout<<*p<<endl;++p;
16、使用预处理进行调试
int main()
{
   #ifndef NDEBUG
   cerr << "starting main" << endl;
   #endif
}
int main(){   #ifndef NDEBUG   cerr << "starting main" << endl;   #endif}如果 NDEBUG 未定义,那么程序就会将信息写到 cerr(输出到控制台)中。如果 NDEBUG 已经定义了,那么程序执行时将会跳过 #ifndef 和 #endif 之间的代码。

posted @ 2011-05-24 14:06  陈朋  阅读(398)  评论(0编辑  收藏  举报