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,因为编译器会在双引号引起的内容后面加上\0

    char HELLOWORLD2[] = {'h','e','l','l','o','w','o','r','l','d','\0'};
  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
    ```c
    using namespace std;
    int main(){
    cout << "Enter your name:";
    string name;
    cin >> name;

     int charAllocate = name.length() + 1;   // 字符串结尾的\0
     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:当前指针变量指向的内存地址上的数据为常量,不可修改。但指针指向的地址可以改变。
    c 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;

  3. 当指针变量拷贝到另一个指针变量时,只需对其中一个指针进行delete,多次delete会导致程序崩溃
    c int* p = new int; *p = 30; int* p1 = p; delete p; delete p1; // 程序崩溃

  4. 检查new发出的请求分配是否得到满足
    (1)默认情况下,C++在请求分配内存失败的情况下,抛出std::bad_alloc异常
    c 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
    ```c
    int original = 30;
    cout << hex << &original<<endl; // 0x7ffd02a9593c

    int& ref = original; //引用变量的值是内存上的数据值
    cout << hex << &ref << endl; // 0x7ffd02a9593c,引用和original变量的地址相同
    ```
  2. 引用的用处
    (1)因为C++函数的参数传递是值拷贝,当参数所占内存很大时,值复制会消耗大量时间。可以把函数的形参设为引用变量,这样在值传递时,引用指向实参地址,不会发生内存拷贝。
    ```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引用,既可以避免内存拷贝,又可以避免实参得治在函数体中被修改
    ```c
    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属性。::符号又被称为作用域解析运算符
    ```c
    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){}的空函数体形式
    ```c
    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)析构函数不能重载,每个类只有一个析构函数,如果没有自定义析构函数,编译器会自动加上一个空的析构函数,这样的话,类中动态申请的内存将无法被释放
    ```c
    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)形式:
    c class ClassName{ public: ClassName(const ClassName& origion){ ... } }

  3. 移动构造函数
    (1)因为C++严格按照复制构造函数进行参数传递,在对象的指针成员指向的缓冲区很大时,深复制会造成效率降低
    (2)所以在提供复制构造函数的同时,需要提供移动构造函数。形式如下:
    c ClassName(ClassName&& src)
  4. 构造函数举例
    ```c
    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. 单例类
    ```c
    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)私有化析构函数后,在函数退出时,不能调用析构方法来销毁对象,所以这样的代码就不能通过编译。
    ```c
    class MonsterDB{
    private:
    ~MonsterDB(); // 私有化析构函数
    };

    int main(){
    MonsterDB myDB; // 编译报错,方法中不能再使用类名创建对象
    return 0;
    }
    ```

  3. this指针
    表示当前对象的内存地址

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

  2. 模板举例
    (1)模板函数
    c 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;

    ```

转载于:https://www.cnblogs.com/moonlord/p/6991442.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值