IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++相关概念(个人)记录 -> 正文阅读

[C++知识库]C++相关概念(个人)记录

本文仅仅对C++相关概念进行粗略记录,具体细节请查阅相关书籍

基本概念

  • 命名空间namespace:在多人合作开发时,防止出现变量或函数的命名冲突

    namespace A {
        int a = 100;
        namespace B            //嵌套一个命名空间B
        {
            int a = 20;
        }
    }
    int a = 200;//定义一个全局变量
    
    int main(int argc, char *argv[]) {
        cout << "A::a =" << A::a << endl;        //A::a =100
        cout << "A::B::a =" << A::B::a << endl;  //A::B::a =20
        cout << "a =" << a << endl;              //a =200
        cout << "::a =" << ::a << endl;          //::a =200
    
        using namespace A;
        cout << "a =" << a << endl;     // Reference to 'a' is ambiguous ,全局a和A::a冲突
        cout << "::a =" << ::a << endl; //::a =200
    
        //全局变量 a 表达为 ::a,用于当有同名的局部变量时来区别两者。
        int a = 30;
        cout << "a =" << a << endl;     //a =30
        cout << "::a =" << ::a << endl; //::a =200
    
        using namespace A;
        cout << "a =" << a << endl;     // a =30  // 当有本地同名局部变量后,优先使用本地,冲突解除
        cout << "::a =" << ::a << endl; //::a =200
    }
    
  • 关键字operator:用于重定义或重载大部分 C++ 内置的运算符详情见此链接

  • 关键字explicit的作用就是防止类构造函数的隐式自动转换

    class exp {
    public:  
        explicit exp(int a) { temp = a; }
        exp();
    private:
        int temp;
    };
    
    /* 若未使用explicit关键字,会发生隐式类型转换,此时不报错
       exp temp(10);
       exp temp_exp = temp;*/
    exp temp_exp = 10;		//explicit关键字声明,报错
    
  • 构造函数(未声明时编译器会自动声明):默认构造函数、copy构造函数。当没有对象被定义,则使用copy assignment操作符

    class copy_class {
    public:
        copy_class();								//默认构造函数
        copy_class(const copy_class &cc);			//copy构造函数
        copy_class& operator=(const copy_class &cc);//copy assignment操作符
    };
    
    copy_class cc1;			//调用默认构造函数
    copy_class cc2(cc1);	//调用copy构造函数
    copy_class cc3 = cc1;   //有新对象被定义,调用copy构造函数
    cc2 = cc1;				//没有新对象被定义,调用copy assignment操作符
    
  • 构造函数使用成员初值列(member initialization list) 替代赋值动作,因为C++规定对象成员变量的初始化动作发生在进入构造函数本体之前

    class exp {
    public:
        explicit exp(int a, double b)
        :   temp(a),
            num(b),
            len(0) {
    
        }
    private:
        int     temp;
        double  num;
        int     len;
    };
    
  • const成员函数:表示该成员函数不会修改成员变量;并且const对象只能调用const成员函数

    class exp {
    public:
        ......
        void print() const {
            std::cout<<"const "<<temp<<" "<<num<<" "<<len<<std::endl;
        }
        void print(int a) {
            len = a;
            std::cout<<"no const "<<temp<<" "<<num<<" "<<len<<std::endl;
        }
    private:
        int     temp;
        double  num;
        int     len;
    };
    
    	const exp const_exp();
        const_exp.print();		//const_exp.print(99);则会报错
        exp no_const_exp();
        no_const_exp.print(99);
    
  • 虚析构函数:为了解决使用父类指针指向子类对象,但在释放子类对象的资源时,调用了父类析构函数;导致子类释放不完全,造成的内存泄漏问题。

  • trythrowcatch:异常是程序在执行期间产生的问题,异常处理提供了一种转移程序控制权的方式。设定一块区域由try监控,当出现异常时throw抛出异常,catch捕获抛出的异常并进行处理。

    	try {
            throw "error test!";
        } catch (const char *str) {
            cerr<<str<<endl;
        }
    
  • 模板:模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码

    • 函数模板,实际上是建立一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。 换个角度说,函数模板除了支持值的参数化,还支持类型的参数化

      template <typename type> ret-type func-name(parameter list)
      {
         // 函数的主体
      }
      
      template <typename T>
      void Swap(T &a, T &b) {
          T temp = a;
          a = b;
          b = temp;
      }
      
      int main(){
          int n1 = 100, n2 = 200;
          Swap(n1, n2);			//n1=200; n2=100;
      
          float f1 = 12.5, f2 = 56.93;
          Swap(f1, f2);			//f1=56.93; f2=12.5;
      }
      
    • 类模板

      template <class T> class class_name {};
      
      template <class T>
      class my_stack {
      public:
          void push(T const &ele);    //入栈
          T* pop();                   //出栈
      private:
          vector<T> elems;
      };
      
      template <class T>
      void my_stack<T>::push(T const &ele) {
          elems.push_back(ele);
      }
      
      template <class T>
      T* my_stack<T>::pop(){
          if ( elems.empty() )
              return NULL;
          T* temp = &elems.back();
          elems.pop_back();
          return temp;
      }
      
      int main()
      {
          class my_stack<int> sta;
          sta.push(10);               sta.push(3);
          cout<<*sta.pop()<<endl;     cout<<*sta.pop()<<endl;	//3  10
          
          class my_stack<string> str;
          string str1 = "hello";      string str2 = "world";
          str.push(str1);             str.push(str2);
          cout<<*str.pop()<<endl;     cout<<*str.pop()<<endl;	//world   hello
      }
      
  • #起到将内容双引号成字符串的作用,## 起到连接连接参数的作用

    #define TRAN_STR(x)     (#x)
    #define CONNECT(x, y)   (x##y)
    string YourName = "what is you name";
    cout<<TRAN_STR(tran to str)<<endl;		//输出:tran to str
    cout<<CONNECT(Your, Name)<<endl;		//输出:what is you name
    
  • 多线程pthread_create (thread, attr, start_routine, arg)

    void* say_hello(void* args) {
        string str = *((string*)args);
        cout << str << endl;
        return 0;
    }
    
    int main()
    {
        string str = "hello world";
        pthread_t th;
        if ( pthread_create(&th, NULL, say_hello, &str) )
            cout<<"create thread error"<<endl;
    
        pthread_exit(NULL);     //等待各个线程退出了,才退出
    }
    
  • protected成员:防止继承后的基类private成员无法访问,于是有了protected,集合了public成员(对派生类)private成员(对外部访问) 的特点。

  • static类成员

    • 初始化的静态变量在data段,未初始化的静态变量在bss段。
    • 静态成员变量属于整个类所有,可以通过类名和对象名直接访问公有静态成员变量。
        class class_name {
      static void static_fun();
      static int static_val;
      ......
      }
      class class_name opera;
      class_name::static_fun();
      opera::static_val = 10;
      
    • 当调用一个对象的非静态成员函数时,系统会把对象的起始地址赋给成员函数的this指针;因为静态成员函数不属于任何一个对象,因此没有this指针,也无法对非静态成员进行访问
    • 静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他数据和函数。
  • 结构体/联合体/类内存对齐原则

    • 设定第一个成员在offset为0的地方,后续的基本数据类型的起始位置是其大小的整数倍,后续结构体/联合体的起始位置是其最大基本数据类型大小的整数倍。

    • 收尾时,总的数据大小必须是其中最大基本数据类型的整数倍(子结构中的基本数据类型也包括)。

      /* 大小为24字节 */
      class exp {
      public:
          static const int CONST_VAL = 100;
          exp(int a);
          exp();
          ~exp();
      private:
          int     temp;	//占4字节
          double  num;	//offset为4,不是整数倍,因此从offset为8开始;所以占用12字节
          int     len;	//占4字节,总大小为20字节,但不是8的倍数;所以总大小为24字节
      };
      
      /* 大小为24字节 */
      union example {
          int  	a[5];
          char 	b;
          double	c;
      };	/* 最大20 Byte,但要能整除8,所以为24 Byte */
      
      /* 大小为80字节 */
      struct example2 {
        	int  	a[4]; 		//占16字节
        	union example e1; 	//占24字节
          char 	b;	  		//占1字节
          union example e2;	//offset为41,不是整数倍,因此从offset为48开始;所以占用31字节
          int16_t c;	  		//offset为72,占用2字节
      }; /* 74 Byte , 但要能整除8,所以为80 Byte*/
      
  • 引用

    • 引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字
    • 与指针区别:int &a = b; 相当于int * const a = &b;
      • 不存在空引用。引用必须连接到一块合法的内存。
      • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
      • 引用必须在创建时被初始化。指针可以在任何时间被初始化。
    • 形参是引用变量,调用函数时,形参(引用变量)指向实参变量单元。这种通过形参引用的方式可以改变实参的值。
  • 友元函数/友元类:可以直接访问类内所有对象,为了实现一些特定的操作,打破了类的封装特性。

    class Box {
    private:
        double width;
    public:
        friend void printWidth(Box box);	//友元函数
        friend class BigBox;				//友元类
        void setWidth(double wid) { width = wid; }
    };
    
    class BigBox {
    public :
        void Print(int width, Box &box) {
            // BigBox是Box的友元类,它可以直接访问Box类的任何成员
            box.setWidth(width);
            cout << "Width of box : " << box.width << endl;
        }
    };
    
    // 请注意:printWidth() 不是任何类的成员函数
    void printWidth(Box box)
    {
        /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
        cout << "Width of box : " << box.width << endl;
    }
    
    // 程序的主函数
    int main()
    {
        Box box;
        BigBox big;
    
        // 使用成员函数设置宽度
        box.setWidth(10.0);
        // 使用友元函数输出宽度
        printWidth(box);
        // 使用友元类中的方法设置宽度
        big.Print(20, box);
        
        return 0;
    }
    

规定

  • 使用编译器替代预处理器:用const、inline、enum替代#define

    • 用于在报错时快速定位错误位置,#define TEMP 3.14出错时,也许会提到3.14但不是TEMP(预处理时被替换了)
    • #define不重视作用域,因此无法创建一个class专属常量(static const int CONST_VAL = 100)。
    • inline函数遵守函数准则,而#define宏函数则只是文本替换,没有函数准则。
  • delete一个指向数组指针,告诉编译器该指针指向一个数组,即new的时候使用[],则delete [] p;最好不要对数组使用typedef,如下

    typedef std::string arr[4];		//每个arr是 4个string 的组合
    std::string *p = new arr;		//相当于new std::string[4]
    delete p;						//不严谨,应该是delete [] arr
    
    int **arr = new int*[10];
    for( int i = 0; i < 10; i ++ )
        arr[i] = new int[5];
    for( int i = 0; i < 10; i ++ )
        delete [] arr[i];
    delete [] arr;
    
  • 尽可能将成员变量声明为private —— 封装的重要性

    • 赋予客户访问数据的一致性
    • 细微划分访问控制
    • 给class维护者充分的实现弹性

STL( standard template library),标准模板库

  • STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离

  • 从根本上说,STL 是一些容器算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的。

  • 迭代器:迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素,与指针类似

    容器类名::iterator 		  			迭代器名;	//正向迭代器
    容器类名::const_iterator  			迭代器名;	//常量正向迭代器
    容器类名::reverse_iterator			迭代器名;	//反向迭代器
    容器类名::const_reverse_iterator  	迭代器名;	//常量反向迭代器
    
    	int i;
        std::vector<int> a;
        for (i = 0; i < 10; i ++)
            a.push_back(i);
        std::vector<int>::iterator p = a.begin();
        for (i = 0; i < 10; i ++) {
            std::cout<<*p<<" ";
            p ++;
        }
        std::cout<<std::endl;
    	std::vector<int>::reverse_iterator rp = a.rbegin();
        for (i = 0; i < 10; i ++) {
            std::cout<<*rp<<" ";
            rp ++;
        }
    	/* 输出: 
    		0 1 2 3 4 5 6 7 8 9
        	9 8 7 6 5 4 3 2 1 0	*/
    

资源管理

  • 智能指针用于解决动态内存管理出现的三种问题:

    • 申请之后忘记释放内存,会造成内存泄漏;
    • 尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针;
    • 内存的二次释放,即对同一个指针进行两次 free() 操作,可能导致程序崩溃;
  • auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。当一个auto_ptr被拷贝或被赋值后,就失去了对原对象的所有权,因此不存在多个auto_ptr指向同一对象。

    	int *p = new int;
        auto_ptr<int> ap(p);
        *ap = 100;
        auto_ptr<int> aap = ap;		//发生拷贝操作,ap失去对原对象的所有权
        cout<<*p<<endl;
    
  • shared_ptr

    • shared_ptr可以将多个指针指向相同的对象(共享)。
    • shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,对象的引用计数加1,每析构一次,对象的引用计数减1,减为0时,自动删除所指向的堆内存。
    • shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁
    • 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存
    /* 创建一个共享智能指针,指向classA对象,初始化为0 */
    shared_ptr<classA>sp = make_shared<classA>(0);
    /* 获取sp中保存的指针 */
    classA *p = sp.get();
    /* 拷贝sp至cp,sp的引用计数加1 */
    shared_ptr<classA>cp (sp);
    /* 返回共享对象的引用指针数量 */
    sp.use_count();
    
  • unique_ptr

    • reset() 函数将重置它,即它将释放delete关联的原始指针并使unique_ptr对象为空
    • 同一时刻只能有一个unique_ptr指向给定对象(通过 禁止拷贝语义、只有移动语义move() 来实现)
  • weak_ptr只能和share_ptr配合使用,把share_ptr赋值给weak_ptr时并不会增加引用计数

参考资料

粗略记录,不当之处请在评论区指出

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:30:29  更:2022-03-21 20:32:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:23:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码