zoukankan      html  css  js  c++  java
  • 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 之间的代码。

  • 相关阅读:
    为什么基于TCP的应用需要心跳包(TCP keep-alive原理分析)
    「DDoS攻击」兴风作浪,教你如何有效防护!
    你还敢乱粘贴吗?
    TODO git如何去掉烦人的merge?
    Git修改已经push到远程的commit信息
    Oracle删除唯一索引失败提示ORA-01418:指定的索引不存在 ORACLE
    mybatis逆向生成代码 [ERROR] No plugin found for prefix 'mybatis-generator' in the current project and in the plugin groups
    MySQL 中 redo log、undo log、binlog 的总结
    VATT: Transformers for Multimodal Self-Supervised Learning from Raw Video, Audio and Text
    OPT: Omni-Perception Pre-Trainer for Cross-Modal Understanding and Generation
  • 原文地址:https://www.cnblogs.com/flysnail/p/2055426.html
Copyright © 2011-2022 走看看