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 =π
cout<<**ppi<<endl;
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 << ' '; // 输出当前对象
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;
int main() { #ifndef NDEBUG cerr << "starting main" << endl; #endif }