C++ 基础


  • 虚函数
    • B是A的子类,A中有虚函数a,
        class Entity
        {    public:
        <!-- 这里加上virtual才能实现多态,否则(Entity *)player->getName()会调用Entity的方法 -->
        virtual std::string GetName() { return "Entity"; }
        };
        class Player : public Entity
      
    • 缺点:
      • 需要额外的内存空间存储虚函数的表,使得指向基类V表中的参数将正确的指针发送给函数。
      • 每当调用虚函数,需要遍历虚函数表查找它映射了哪个函数
    • 纯虚函数
  • 可见性
    • see,use
    • private(只有自己和friend可以访问), protected(只有自己和派生类可以访问,实体类都无法访问), public()
    • 为了人类自己的方便而定义的
        class Entity{
            //private
            int x, y;};
        struct Entity{
            //public
            int x, y;};
      
  • 数组
    • 数组的名字实际上是一个指针
    • &e 可以用这个在visual studio 的Memory中查找地址
        static const int size = 5;
        int example[size];
        不能 example.size();
        // C++ 11 新规范
        std::array<int, 5> another;
        可以 another.size();
      
  • 字符串
    • 字符是什么?一个byte的内存,UTF-8,256种,UTF-16,16个位
    • 字符串以 00 结尾,指示该字符串结束
        std::string name = std::string("yuqin") + "hello"; 
        <!-- 或者下面两行这样 s 表示一个函数 -->
        using namespace std::string_literals;
        std::string name0 = "Cherno"s + "hello";
        bool contains = name.find("no") != std::string::npos;
      
    • 当传入一个字符串作为参数的时候,是将字符串进行拷贝
  • 字符串面量
      <!-- 只能这样使用char *表示字符串 -->
      char *name = (char*)"Che\0rno";
      const char name[8] = "Che\0rno";
      std::cout << strlen(name) << std::endl;
      <!-- 这样加R可以换行 -->
      const char* example = R"(Line1
      Line2
      Line3)";
    
  • cosnt
    • const位置
        int* const a = new int;  // 可以改变指针内容,但是不能改变自己
        const int*  a = new int;  // 不能改变指针内容,但是能改变自己
        // 返回一个不能改变的指针,指针的内容不能被改变,这个方法不能改变Entity实体类
        const int* const Getx() const {
        return _x;
        }
        int* _x, _y; // 只有_x是指针,
        // 这个不加cosnt无法使用 void PrintEntity(const Entity& e) {
        int Getx() const {
        return _x; 	}	
        void PrintEntity(const Entity& e) {
        std::cout << e.Getx() << std::endl;
        }
        <!-- mutable定义的变量可以在const函数中被改变 -->
        mutable int var;
        int Getx() const {
            var = 2; }
      
  • mutable (少见)
      int x = 8;
      // lambda
      auto f = [=]() mutable{
          x++;  // 创建一个新的变量,
          std::cout << x << std::endl;
      };
      f();
    
  • 类初始化列表
      Entity()  : m_Name("unKnown"), m_Score(0)	{	}
      <!-- 普通创建example会有两次 -->
      class Example {
      public:
          Example() {
              cout << "create example! " << endl;
          }
          Example(int x) {
              cout << "create example " << x << " !" << endl;
          }
      };
      class Entity {
      private:
          Example example;
      public:
          Entity()
          {
              example = Example(8);
              /*create example!
              create example 8 !*/
          }
          <!-- 换成初始化列表只会生成一次 -->
          Entity(): example(8)
              {                /*create example 8 !*/            }
          };
      };
    
  • Ternary operator
    • 三目运算符,替代 if else。
  • 创建一个对象
    • Entity e; 这样就是一个初始化的合法的代码,不像java需要new。
        Entity e = Entity("Cherno");
        <!-- 生命周期,{}语句块结束之后e仍然指向那个相同地址,但是"cherno"已经被销毁 -->
        Entity *e;
        {
            //在stack上分配
            Entity entity("Cherno");
            e = &entity;
            cout << entity.GetName() << endl;
        }
        <!-- new分配heap上的内存 -->
        // new 非常重要,在heap上分配内存,耗时,不会自动回收
        Entity* entity = new Entity("Cherno");
        delete entity;
        <!-- 不会自动销毁 -->
        Entity *e;
        {
            // new 非常重要,在heap上分配内存
            Entity* entity = new Entity("Cherno");
            cout << (*entity).GetName() << endl;
        }
      
  • new
    • 内存、性能、优化
    • new在heap上分配内存;
        int a = 2;
        int* b = new int; //4bytes;
        int* c = new int[50]; // 200bytes
        delete b;
        delete[] c;
        <!-- 相同 -->
        Entity* e = new Entity();
        Entity* e = (Entity*)malloc(sizeof(Entity));
        <!-- 指定new的分配地址 -->
        int* c = new int[50]; // 200bytes
        Entity* e = new(c) Entity(); //假设Entity有60字节?指定Entity分配的内存地址
      
  • 隐式转换和explicit关键字
      <!-- 隐式转换 -->
      Entity(const string& name) : m_Name(name) {}
      Entity(int age) :m_Name("Unknown"), m_Age(age) {}
      Entity a = string("Cherno"); // 可以运行但不建议
      Entity b = 22; //可以运行但不建议
      Entity b(22); //建议
      <!-- explicit强制必须是该类的构造,而不是隐式转换 -->
      explicit Entity(int age) :m_Name("Unknown"), m_Age(age) {}
      Entity b = 22;  // 不行,错误
      Entity b = (Entity)22;  //可以
    
  • 操作符
    • 重载
        <!-- 重载 << 操作符  -->
        ostream& operator << (ostream& stream, const Vector2& other) {
        stream << other.x << "," << other.y;
        return stream;
        <!-- 重载, +操作符 -->
        Vector2 operator+(const Vector2& other) const {
        return  Vector2(x + other.x, y + other.y);
        }
        Vector2 result2 = position + speed;
      
  • this
  • 对象的生命周期
    • 作用域对栈中的对象有用,New出来的对象在heap上,不受作用域限制
    • 智能指针的启发:
        class Entity {
        public :
        int x, y;
        Entity(){
            cout << "create" << endl;
        }
        ~Entity() {
            cout << "destoryed" << endl;
        }
        };
        class ScopePtr {
        private:
            Entity * m_Ptr;
        public:
        ScopePtr(Entity* ptr) :m_Ptr(ptr) {
      
        }
        ~ScopePtr() {
            delete m_Ptr;
        ;	}
        };
        int main()
        {
        {
            ScopePtr e = new Entity();
        }
      
  • 智能指针
      // unique
      std::unique_ptr<Entity> e0 = std::make_unique<Entity>();
      std::unique_ptr<Entity> e1 = e0; // 非法
      <!-- shared -->
      {
          std::shared_ptr<Entity> e0;
          {
              std::shared_ptr<Entity> shar_ptr = make_shared<Entity>();
              e0 = shar_ptr;
          } //如果是weak_ptr,则在这里销毁
      } //这个结束Entity才销毁
    
  • 复制和复制构造函数
    • =就是复制 ```C++ struct Vector2 { float x, y; }; Vector2 a = { 2,3 }; Vector2 b = a; b.x = 5; // a {2,3} ; b{5,3} class String { private: char* m_Buffer; unsigned int m_Size; public: String(const char* string) { m_Size = strlen(string); m_Buffer = new char[m_Size + 1]; memcpy(m_Buffer, string, m_Size); m_Buffer[m_Size] = 0; } // 深拷贝 String(const String& other) : m_Size(other.m_Size){ m_Buffer = new char[m_Size + 1]; memcpy(m_Buffer, other.m_Buffer, m_Size + 1); } ~String() { delete[] m_Buffer; } char& operator { return m_Buffer[index]; } friend std::ostream& operator«(std::ostream & stream, const String& string); };

    std::ostream& operator«(std::ostream & stream, const String& string) { stream « string.m_Buffer; return stream; } struct Vector2 { float x, y; }; int main() { String string = “yuqin”; String second = string; second[2] = ‘a’; cout « string « endl; cout « second « endl;

      std::cin.get();   }   ```
    
  • 箭头符号
    • 自动delete代理。//ziji:代理这个词用得不错 ```C++ class Entity { private: int x; public: void Print() const { cout « “Hello “ « endl; } };

    class ScopedPtr { private: Entity* m_Obj; public: ScopedPtr(Entity* entity) :m_Obj(entity){

      }
      ~ScopedPtr() {
          delete m_Obj;
      }
      Entity* GetObject() { return m_Obj; }
      <!-- 第二种方法,重载箭头 -->
      const Entity* operator->() const{ return m_Obj; }   };   int main()   {	
      ScopedPtr entity = new Entity();
      entity.GetObject()->Print();
      <!-- 第二种方法 -->
      const ScopedPtr entity = new Entity();
      entity->Print();
      std::cin.get();   }   <!-- 利用->找到偏移值 -->   struct Entity {
      int x, y, z;   };   int main()   {	
      int offset = (int)&((Entity*)0)->x;
      cout << offset << endl;
      std::cin.get();   }   ```
    
  • 动态数组,vector
    • stl
    • 如何优化使用vector
      • 首先再main函数得stack中构建一个vector,然后需要拷贝到类的空间中,
          <!-- 拷贝3次 -->
          vector<Vertex> vertices;
          vertices.reserve(3);
          vertices.push_back(Vertex{ 1,2,3 });
          vertices.push_back(Vertex{ 4,5,6 });
          vertices.push_back(Vertex(7, 8, 9));
          <!-- 使用emplace_back直接在vertex的堆上生成 -->
          vector<Vertex> vertices;
          vertices.reserve(3);
          vertices.emplace_back(1,2,3 );
          vertices.emplace_back( 4,5,6 );
          vertices.emplace_back(7, 8, 9);
        
    • GLFW,include,library
    • 第一,在C++的general中设置additional include directories为项目相对路径下的glfw的include文件夹,第二,在Linker的general中设置additional library directories文件夹,并设置input中的additional dependencies.
        #include <GLFW/glfw3.h>
        using namespace std;
        int main()
        {	
        int a = glfwInit();
        cout << a << endl;
        std::cin.get();
        }
      
  • 返回多个不同种类的值
    • 使用struct,带引用的参数
  • template
      template<typename T>
      void Print(T value) {
          cout << value << endl;
      }
      Print(5); // C++自动知道是int类型
      Print<string>("hello");
      <!-- 利用template初始化数组 -->
      template<typename T, int N>
      class Array {
      private:
          T m_Array[N];
      public:
          int GetSize() const { return N; }
      };
      Array<int,5> array;
      cout << array.GetSize() << endl;
    
    • ziji:感觉C++非常灵活。
    • 在Log系统中可以用
  • stack和heap
    • RAM中的两块内存区域
    • 在stack中执行分配和赋值操作特别简单,asm汇编语句只有一句话,而在heap上分配则非常麻烦,所以在stack的内存足够情况下,请优先使用stack的内存。
    • 单纯的文字替换
    • 用来ifdef else,宏替换,在debug和release状态切换
  • auto
    • 简化代码,在迭代的时候用比较好
        using DeviceMap = std::unordered_map<std::string, std::vector<Device*>>;
        typedef std::unordered_map<std::string, std::vector<Device*>> DeviceMap;
        auto DeviceMap = Get();
      
  • 函数指针
    • 不是关键字的可以作为一个函数指针
        void PrintValue(int value) {
        cout << "value:" << value << endl;
        }
        void ForEach(const vector<int>& values, void(*func)(int)) {
        for (int value : values)
            func(value);
        }
        int main()
        {	
        vector<int> values = { 1,5,3,2,4 };
        ForEach(values, PrintValue);
         <!-- lambda -->
        ForEach(values, [](int value) {cout << "value" << value << endl; });
        std::cin.get();
        }
      
  • lambda表达式可以用来表示函数指针
    • []里面表示如何传入参数,[=]表示传输值,[\&]表示传入地址,或者[&a,b]表示a传地址,b传值
  • using namespace std
    • 为什么不用这个?清晰显示来源
    • 什么是namespace,避免naming conflict
  • thread
      static bool s_Finished = false;
      void DoWork() {
          using namespace std::literals::chrono_literals;
          std::cout << "started thread id = " << std::this_thread::get_id() << std::endl;
          while (!s_Finished) {
              std::cout << " working" << std::endl;
              std::this_thread::sleep_for(1s);
          }
      }
      int main()
      {	
          std::thread worker(DoWork);
          //worker.join(); // join=等待worker完成
          std::cin.get();
          s_Finished = true;
          worker.join();
          std::cout << "finished" << std::endl;
          std::cin.get();
      }
    
  • Timing
    • 巧用time计时
        struct Timer {
        std::chrono::time_point<std::chrono::steady_clock> start, end;
        std::chrono::duration<float> duration;
      
        Timer() {
            start = std::chrono::high_resolution_clock::now();
        }
      
        ~Timer() {
            end = std::chrono::high_resolution_clock::now();
            duration = end - start;
            float ms = duration.count() * 1000.0f;
            std::cout << "Time took " << ms << " ms " << std::endl;
        }
        };
        void Function(){
        Timer timer;
        for (int i = 0; i < 100; i++)
            std::cout << "hello " << std::endl;
        }
        int main()
        {	
        Function();
      
        std::cin.get();
        }
      
  • 多维数组
    • int**(指向int *的指针)
  • sort
    • 注意包含的头文件 ```C++ #include #include #include

    int main() { std::vector values = { 3,5,1,2,4 }; std::sort(values.begin(), values.end(), [](int a, int b){ return a > b; }); std::sort(values.begin(), values.end(), std::greater()); for (int value : values) std::cout << value << std::endl; std::cin.get(); } ```

  • 类型双关type punning
    • C++可以直接得到内存,
        struct Entity {
        int x, y;
        };
        int main()
        {	
        Entity entity = { 1,3 };
        int *position = (int*)&entity; //position 是一个 int 的数组,将地址解析为另一种类型
        int y = *(int*)((char*)&entity + 4);
        std::cout << position[0] << "," << position[1] << std::endl;  //1,3
        std::cin.get();
        }
      
  • union
    • 共享内存
        struct Vector2 {
        float x, y;
        };
        struct Vector4 {
        union {
            struct {
                float x, y, z, w;
      
            };
            struct {
                Vector2 a, b;
            };
        };
        };
        void PrintVector2(const Vector2& vector) {
        std::cout << vector.x << ", " << vector.y << std::endl;
        }
        int main()
        {	
        Vector4 vector = { 1.0f, 2.0f, 3.0f,4.0f };
        PrintVector2(vector.a);
        PrintVector2(vector.b);
        vector.z = 500.0f;
        PrintVector2(vector.a);
        PrintVector2(vector.b);
        std::cin.get();
        }
      
  • virtual destructions
    • 如果没有这个,只是单纯实现两个析构函数,则可能导致内存泄漏 ```C++ class Base { public: Base() { std::cout « “Base Constructor\n”; } ~Base() { std::cout « “Base Destructor\n”; } };

    class Derived :public Base { public : Derived() { m_Array = new int[5]; std::cout « “Derived Constructor\n”; } ~Derived() { delete[] m_Array; std::cout « “Derived Constructor\n”; } private: int* m_Array; }; int main() { Base* base = new Base(); delete base; std::cout « ”————-\n”; Derived* derived = new Derived(); delete derived; std::cout « ”————-\n”; Base* poly = new Derived(); delete poly; std::cout « ”————-\n”; std::cin.get(); } Base Constructor Base Destructor ————- Base Constructor Derived Constructor Derived Constructor Base Destructor ————- Base Constructor Derived Constructor Base Destructor 只销毁base,出现内存泄露 ————- virtual ~Base() { std::cout « “Base Destructor\n”; } // 这样就好 Base Constructor Derived Constructor Derived Constructor Base Destructor ———— ```

  • casting
    • 几种
  • 调试
    • action可以在console中显示当前的变量的值。不需要断点查看。
    • condition可以在一定条件下出发断点。
  • safety
    • 智能指针
  • 预编译
    • 节省时间,需要设置。emmm,没记下来
  • dynamic casting
  • benchmarking

    • visual studio 转到反汇编, ALT+G,确认需要编译的没有被优化
    • project->c++->language->language standard
        std::optional<std::string> ReadFileAsString(const std::string& filepath) {
        std::ifstream stream(filepath);
        if (stream) {
        std::string result;
        stream.close();
        return result;
        }
        return {};
        }
        int main() {
        std::optional<std::string> data = ReadFileAsString("data.txt");
        if (data.has_value()) {
            std::cout << "File read successfully";
        }
        else {
            std::cout << "File can not be opened!!";
        }
        std::cout << "adf" << std::endl;
        }
      
  • 使用any类型
    • 少用
  • 加速
    • 使用并行的for loop。
    • mutex,互斥锁,lock该资源
        static std::mutex s_Mutex;
        static void Load(vector<Ref<Mesh>> & meshes, string filepath) {
        auto mesh = Mesh::load(filepath);
        lock_guard<std::mutex> lock(s_Mutex);
        meshes.push_back(mesh);
        }
      
  • 字符串优化
    • 使用c_str,使用const char *,使用string_view
        static uint32_t s_AllocCount = 0;
        void* operator new(size_t size) {
        s_AllocCount++;
        std::cout << "Allocating " << size << " bytes \n";
        return malloc(size);
        }
        void PrintName(std::string_view name) {
        //void PrintName(const std::string& name) {
        std::cout << name << std::endl;
        }
        int main() {
        std::string name("Wangyuqin Shuicahoyang");  // string的构造函数会先分配8个字节,如果右值大于8字节,则会allocate。
        std::string filename = name.substr(0, 4);
        std::string lastname = name.substr(5, 10);
        std::string_view firstName(name.c_str(), 3);
        std::string_view lastName(name.c_str() + 4, 4);
              
        const char* name = "Wangyuqin Shuichaoyang";
        std::string_view firstName(name, 3);
        std::string_view lastName(name + 4, 4);
        PrintName("Wang Yuqin");
        std::cout << s_AllocCount << " allocation" << std::endl;
        std::cin.get();		
        }
      
  • singleton
    • 随机数产生的class适合用单例模式 ```C++ class Random { public: Random(const Random&) = delete; static Random& Get() { return s_Instance; } float Float() { return m_Random; } private: Random() {} float m_Random = 0.5f; static Random s_Instance; };

    Random Random::s_Instance; int main() { float number = Random::Get().Float(); std::cout « number « std::endl; std::cin.get(); } ```

  • 左值和右值
      int& GetValue() {
          static int value = 10;
          return value;
      }
    
      void SetValue(const int & i) {
          return ;
      }
      int main() {
          const int i = GetValue(); // 左值一定有内存,non const的
          GetValue() = 5;
          SetValue(19);
          SetValue(i); //一个const的左值可以接受const和non const的右值。
          std::cin.get();		
      }
    
  • 静态检查工具
    • PVS-Studio
  • C++参数计算顺序
    • 没有固定顺序,C++17令顺序是一个接着一个,不能是并行输入的,但是从左至右计算还是从右至左计算是不确定的。
        void PrintSum(int a, int b) {
        std::cout << a << " + " << b << " = " << (a + b) << std::endl;
        }
        int main() {
        int value = 0;
        PrintSum(value++, value++);
        std::cin.get();		
        }
      

 Toc
 Tags