• C++21天学会


    21天学会C++

    标签(空格分隔): c语言


    第一章 C++程序组成部分

    一. Hello World程序的组成部分

    1. C++中的helli-world

      #include <iostream>
      int main(){
          std::cout << ”HELLO WORLD“ << std::endl;
      }
      
    2. 预处理器编译指令#include:
      预处理器是编译前运行的工具,#include<filename>让处理器获取指定文件的内容,并将这些代码放在编译指令所处的位置

    3. 返回值:
      很多情况下,一个应用程序被另一个程序启动,而父应用程序想知道子应用程序是否成功完成了任务,程序员可通过使用main的返回值向父应用程序传递成功或错误状态

    4. 名称空间:
      (1)代码中使用std::out而不是cout,原因在于cout位于标准名称空间中(std)。假设调用cout时没有使用空间限定符,且编译器知道cout位于两个地方,那编译器应该调用哪个呢,这显然会让编译器混乱。
      (2)代码中频繁添加std限定符很繁琐,为避免添加限定符,可以使用声明using namespace

      using namespace std;
      cout << "hello world" << endl
      

    (3)using不只可以用于限定命名空间这么大的范围,还可以限定到使用的元素
    c using std::cout; using std::endl; cout << "hello world" << endl;

    二. 使用typedef替换变量类型

    1. typedef关键字
      (1)C++允许将变量类型替换为您认为方便的名称
      (2)格式为: typedef origionType desType

      typedef unsigned int STRICTLY_OSITIVE_INTEGER
      STRICTLY_OSITIVE_INTEGER postNumber = 4532
      

    三. C风格字符串与C++风格字符串

    1. C风格字符串
      (1)C风格的字符串用字符数组来声明,而数组都是需要提前计算好元素个数。
      (2)字符数组的元素个事应该为字符串中字符个数+1,因为编译器会在双引号引起的内容后面加上

      char HELLOWORLD2[] = {'h','e','l','l','o','w','o','r','l','d',''};
      
    2. C++风格字符串
      (1)C风格字符串带来的威胁:C语言程序中的strcpy,strcat,strlen等函数,都会寻找终止空字符,如果程序员没有在字符数组末尾添加空字符,这些函数将产生字符数组越界。
      (2)C++提供std::string,这是一种强大而安全的字符串操作方式,他能动态扩展存储大小。

      #include <iostream>
      int main(){
          std::string HELLOWORLD2 = "hello world";
          std::cout << HELLOWORLD2 << std::endl;
      }
      

    第二章 指针和引用

    C++程序可以在字节和比特级调整应用程序的性能,要编写高质量的程序利用系统资源,理解指针和引用就必不可少。

    一. 指针

    1. 声明指针
      (1)指针通常声明为指向特定类型,如int,表示指针所示的内存单元存储了一个整数
      (2)也可让指针声明为指向一个内存块,这种指针被称为void指针
      (3)只声明指针,而不去初始化指针,是给指针赋予了一个垃圾值。因为指针包含的值被视为地址,所以未初始化的指针会导致程序访问非法内存,进而导致程序崩溃

      int *pInteger = NULL;
      
    2. &符号获取变量地址
      所有变量都在内存的一个地址上,可以通过&varialbe来获取变量所处地址。

      #include <iostream>
      int main(){
          int Age = 30;
          int* addr = &Age;
          std::cout << "Integer is at: 0x" << std::hex << &Age << std::endl; // Integer is at: 0x0x7fff1422250c
          std::cout << "Addr is: 0x" << std::hex << addr << std::endl;       // Addr is: 0x0x7fff1422250c
      
      }
      
    3. 使用解除引用运算符*访问指针指向的数据

      int Age = 30;
      int* addr = &Age;
      cout << dec << *addr << endl;
      

    二. 动态内存分配

    1. new动态分配内存,delete动态释放内存
      (1)使用new来分配新的内存块,需要指定为哪种数据类型分配内存
      (2)语法:TypeName* p = new TypeNamedelete p
      (3)分配连续内存:TypeName* p = new TypeName[length]delete[] p

      using namespace std;
      int main(){
          cout << "Enter your name:";
          string name;
          cin >> name;
      
          int charAllocate = name.length() + 1;   // 字符串结尾的
          char* copyName = new char[charAllocate];
      
          strcpy(copyName,name.c_str());          // 将字符串转换为c类型字符串再拷贝给字符数组
          cout << "Alloacated buffer contains:" << copyName << endl;
          delete[] copyName;
      }
      /**
      Enter your name:lj
      Alloacated buffer contains:lj
      */
      
    2. const关键字用于指针的三种形式
      (1)const TypeName* p:当前指针变量指向的内存地址上的数据为常量,不可修改。但指针指向的地址可以改变。

      int Age=30;
      const int* p = &Age;
      //*p = 40;  // 编译报错,这样已经改变了指针所指向内存的数据
      int num2 = 35;
      p = &num2;  // 改变指针指向的地址可以,编译通过
      cout << dec << *p; 
      

    (2)TypeName* const p:指针指向的地址为常量,不可修改,但可修改指向地址上的数据值
    c int Age=30; int* const p = &Age; *p = 40; // 改变地址上的值,编译通过 //int num2 = 35; //p = &num2; // 改变指针指向的地址,编译不通过 cout << dec << *p;
    (3)const TypeName* const p:指针包含的地址,和改地址指向的值都不能修改
    c int Age=30; const int* const p = &Age; // *p = 40; // 不能改变地址上的值 //int num2 = 35; //p = &num2; // 不能改变指针指向的地址 cout << dec << *p;

    1. 当指针变量拷贝到另一个指针变量时,只需对其中一个指针进行delete,多次delete会导致程序崩溃

      int* p = new int;
      *p = 30;
      int* p1 = p;
      delete p;  
      delete p1;  // 程序崩溃
      
    2. 检查new发出的请求分配是否得到满足
      (1)默认情况下,C++在请求分配内存失败的情况下,抛出std::bad_alloc异常

      try{
          int* p = new int[9999999999];
          delete[] p;
      } catch(bad_alloc){
          cout << "Bad allocation" << endl;
      }
      

    (2)使用new(nothrow)在内存分配失败的情况下返回NULL
    c int* p = new(nothrow) int[9999999999]; if(p) delete[] p; else cout << "allocate fail"<<endl;

    三. 引用

    1. 引用是什么
      (1)引用是一个变量的别名,应用初始化时,应让它指向一个变量
      (2)语法格式
      TypeName original = value
      TypeName& ref = original
      int original = 30;
      cout << hex << &original<<endl; // 0x7ffd02a9593c
      
      int& ref = original;  //引用变量的值是内存上的数据值
      cout << hex << &ref << endl;    // 0x7ffd02a9593c,引用和original变量的地址相同
      
    2. 引用的用处
      (1)因为C++函数的参数传递是值拷贝,当参数所占内存很大时,值复制会消耗大量时间。可以把函数的形参设为引用变量,这样在值传递时,引用指向实参地址,不会发生内存拷贝。
      using namespace std;
      
      void square(int& number){
          number = number * number;
      }
      
      int main(){
          int number = 10;
          cin >> number;   // 输入5
      
          square(number);
          cout << number <<endl;  // 25
      
      }
      
    3. const用于引用
      (1)const TypeName& ref = origion:禁止通过引用修改原先变量的值
      (2)把函数形参设置为const引用,既可以避免内存拷贝,又可以避免实参得治在函数体中被修改
      int square(const int& number){
          // number = number * number; 编译错误,禁止通过引用修改变量值
          return number*number;
      }
      
      int main(){
          int number = 10;
          cin >> number;
      
          int result = square(number);
          cout << result <<endl;
      }
      
      

    第三章 类

    一. 造函数与析构函数

    1. 构造函数与setter,getter
      (1)构造方法写在public代码块内
      (2)Person::name:表示在Person类中生命的name属性。::符号又被称为作用域解析运算符
      using namespace std;
      
      class Person{
      private:
          string name;
          int age;
      public:
          Person(int age,string name){     // 写在public内的构造函数
              this->name = name;
              this->age = age;
          }
      
          const string &getName() const {  // setter与getter
              return name;
          }
      
          void setName(const string &name) {
              Person::name = name;
          }
      
          int getAge() const {
              return age;
          }
      
          void setAge(int age) {
              Person::age = age;
          }
          void introduceInfo(){  // 表示在Person类中生命的name属性。::符号又被称为作用域解析运算符
              cout << "My name is:"<< Person::name << " , age is:"<< Person::age << endl;
          }
      };
      int main(){
          Person* p = new Person(23, "zhangsan");
          p->introduceInfo();
      }
      
    2. 构造函数的简写形式
      (1)使用Person(int gae,string name):name(name),age(age){}的空函数体形式
      class Person{
      private:
          string name;
          int age;
      public:
          Person(int gae,string name):name(name),age(age){}  // 构造函数简写形式
      
          void introduceInfo(){
              cout << "My name is:"<<Person::name<<" , age is:"<<Person::age << endl;
          }
      };
      int main(){
          Person* p = new Person(23, "zhangsan");            // new返回开辟的内存空间地址起始
          p->introduceInfo();
      }
      
    3. 析构函数
      (1)析构函数调用时机:当对象不再在作用域内或被delete删除时,对象被销毁,进而调用析构函数
      (2)析构函数不能重载,每个类只有一个析构函数,如果没有自定义析构函数,编译器会自动加上一个空的析构函数,这样的话,类中动态申请的内存将无法被释放
      class MyString{
      private:
          char* buffer;
      public:
          MyString(const char* input){
              if (input!= NULL){
                  this->buffer = new char[strlen(input)+1];
                  strcpy(buffer,input);   // 拷贝input到buffer
              }else{
                  buffer = NULL;
              }
          }
      
          ~MyString(){
              cout<< "mystring clearn up!" <<endl;
              delete[] this->buffer;
          }
      };
      
      int main(){
          MyString* ms = new MyString("helloworld");
          delete ms;    // 对象被销毁,自动调用析构函数
      }
      

    二. 对象的深浅复制

    1. 对象浅复制
      (1)当一个对象作为函数参数进行传递时,C++的值拷贝只是对象的浅拷贝:只复制其指针成员,不复制指针指向的缓冲区。使得在方法退出时,对象被delete同时也把包含的指针成员指向的缓冲区delete了。当手动delete原函数时,原先的缓冲区已经被delete。会出现错误。
      (2)这种错误一般在ms visual stdio模式下会报错

    2. 复制构造函数
      (1)当程序员手动增加了复制构造函数后,编译器在函数调用时,会使用复制构造函数来创建对象,进行对象值传递。
      (2)形式:

      class ClassName{
      public:
          ClassName(const ClassName& origion){ ... }
      }
      
    3. 移动构造函数
      (1)因为C++严格按照复制构造函数进行参数传递,在对象的指针成员指向的缓冲区很大时,深复制会造成效率降低
      (2)所以在提供复制构造函数的同时,需要提供移动构造函数。形式如下:

      ClassName(ClassName&& src)
      
    4. 构造函数举例

      class MyString{
      private:
          char* buffer;
      public:
          // 构造函数
          MyString(const char* input){
              if (input!= NULL){
                  this->buffer = new char[strlen(input)+1];
                  strcpy(buffer,input);   // 拷贝input到buffer
              }else{
                  buffer = NULL;
              }
          }
      
          // 复制构造函数
          MyString(const MyString& origion){
              cout << "copy constructor runing.." << endl;
              if(origion.buffer != NULL){
                  buffer = new char[strlen(origion.buffer) + 1];
                  strcpy(buffer,origion.buffer);
                  cout << "buffer points to:0x" << hex << (unsigned int*)buffer << endl;
              }
              else
                  buffer = NULL;
          }
      
          // 移动构造函数
          MyString(MyString&& origion){
              cout << "move constructor running" << endl;
              if(origion.buffer != NULL){
                  buffer = origion.buffer;   // 移动后的对象指向原先的缓冲区
                  origion.buffer = NULL;     // 对象已移动,原先指针废弃
              }
          }
      
          char* getbuffer(){
              return buffer;
          }
      };
      
      void useMyString(MyString input){   //此时参数传递自调用复制构造函数进行对象生成
          cout << "input is"  << endl;
      }
      
      int main(){
          MyString ms("hello world");
          useMyString(ms);
      }
      /**
          copy constructor runing..
          buffer points to:0x0xe78050
          input is
      */
      

    三. 构造函数与析构函数的其它用途

    1. 单例类

      using namespace std;
      
      class President{
      private:
          President(){};    // 私有化所有造方法
          President(const President&); // 私有化复制构造方法
          const President& operator = (const President&);
          string name;
      
      public:
          static President& instant(){        // 暴露一个static的返回引用的方法
              static President onlyInstance;  // 初始化一个静态对象
              return onlyInstance;
          }
          string getName(){
              return name;
          }
          void setName(string inputName){
              name = inputName;
          }
      };
      
      int main(){
          President& onlyInstance = President::instant();
          onlyInstance.setName("lj");
          cout << "President's name:" << onlyInstance.getName() << endl;         // President's name:lj
          cout << "President's name:" << President::instant().getName() << endl; // President's name:lj
      }
      
    2. 不允许在栈中创建的类
      (1)方法中没有用new产生的类就会在栈里面,要想不让栈中产生类,需要私有化析构函数
      (2)私有化析构函数后,在函数退出时,不能调用析构方法来销毁对象,所以这样的代码就不能通过编译。

      class MonsterDB{
      private:
          ~MonsterDB();   // 私有化析构函数
      };
      
      int main(){
          MonsterDB myDB;   // 编译报错,方法中不能再使用类名创建对象
          return 0;
      }
      
    3. this指针
      表示当前对象的内存地址

    4. C++中结构体与类
      (1)关键字struct来自于C语言,在C++编译器看来,它与类相似,差别仅在于struct中的属性默认全部是public的,除非显式指定,否则struct以公有类方式继承。
      (2)代码示例

      struct Human{
          // 构造函数
          Human(const string& inputname,int inputage):name(inputname),age(inputage){};
      private:
          int age;
          string name;
      };
      
      int main(){
          Human fistMan("lj",26);
      }
      

    第四章 模板

    一. 使用#define定义宏函数

    1. 宏函数定义
      (1)宏函数通常用于非常简单的计算
      (2)宏不考虑数据类型,因此使用宏函数编程很危险

      using namespace std;
      #define MAX(a,b) (a>b?a:b)
      int main(){
          cout << MAX(3,8) << endl;  // 8
      }
      
    2. 使用assert宏函数验证表达式
      (1)引入assert函数先导入assert.h文件

      #include <assert.h>
      int main(){
          char* sayHello = new char[25];
          // sayHello不是空,报错:int main(): Assertion `sayHello == NULL' failed.
          assert(sayHello == NULL);  
          delete[] sayHello;
      }
      
    3. 宏函数的优缺点:
      (1)优点:宏函数将在编译前就地展开,因此简单宏的性能优于函数调用。因为它避免了创建函数栈,传递参数等高开销cpu
      (2)缺点:宏函数不支持任何形式的类型检查。

    二. 模板

    1. 模板声明
      (1)模板让程序员可以定义一种适用于不同类型对象的行为
      (2)模板方法声明:

      template <tyoename T1,typename T2>     // template参数声明
      bool fun1(const T1& p1,const T2& p2);
      

    (3)模板类声明:
    c template <typename T1,typename T2> // template参数声明 class Test{ private: T1 obj1; T2 obj2; public: T1 getObj1(){ return obj1; } };

    1. 模板举例
      (1)模板函数
      using namespace std;
      // 模板函数
      template <typename T>
      const T& getMax(const T& v1,const T& v2){
          if (v1 > v2)
              return v1;
          else
              return v2;
      }
      int main(){
          int v1 = 25;
          int v2 = 35;
          int max = getMax(v1,v2);   // 模板函数调用编译器可自行推断参数类型
          int max2 = getMax<int>(v1,v2); // 模板函数调用也可自行指定参数类型
          cout << max2 << endl;
      }
      

    (2)模板类
    ```c
    template
    class Person{
    private:
    T1 name;
    T2 age;
    public:
    Person(const T1& name,const T2& age){
    this->name = name;
    this->age = age;
    }
    const T1& getName() const{
    return name;
    }
    const T2& getAge() const{
    return age;
    }
    };

    int main(){
        Person <char*,int> p ("zhangsan",23); // 具体化模板类可以手动指定类型
        cout << p.getName() << endl;
        cout << p.getAge() << endl;
    
        Person <> p1 ("lj",26);  // 由于模板参数中给出了默认类型,所以p1的具体化可以使用空类型
        cout << p1.getName() << endl;
        cout << p1.getAge() << endl;
    ```
  • 相关阅读:
    Ext文本框添加清除图标,
    gird鼠标移动显示tip
    shapefile文件导入mysql数据库
    百度、高德、谷歌、火星、wgs84(2000)地图坐标相互转换的JS实现
    POSTGIS修复错误数据
    地图瓦片切片方案
    mapbox.gl源码解析——基本架构与数据渲染流程
    高斯克吕格与地理坐标相互转换算法(JS版本)
    mysql空间扩展对比postgis
    从maven central下载javax.media.jai_core:1.1.3时出错
  • 原文地址:https://www.cnblogs.com/moonlord/p/6991442.html
Copyright © 2020-2023  润新知