c++基本语法

1|0(一)、基本概念


  1. 面向对象:其实质是从对象的角度出发

  2. 面向过程:时间维度

    面向对象:事物维度

  3. 什么是面向对象?

    • 我们就所有对象分为一个个的类
    • 类是定义了对象的属性和操作
    • 对象至少属于某一个类
    • 类与类之间可以继承等关系
    • 对象可以包含对象,告诉对象做什么(而不是how to do?)
  4. 计算机学科与自然学科的不同?

    • 自然学科:先有对象,再有类
    • 计算机学科:现有类,再有对象(人为的学科)
  5. 松耦合:可替换

  6. 封装(interface)

    好处:

    • 便于交流(交流外部)
    • 保护内部(隐藏内部)

    实例:将数据用private修饰,用getter,setter修饰

  7. 当一个类没有归到特定的namespace时,它将归属于一个default namespace

  8. :::解析符(可以看做是一个域的限制)

    如:cat::say()表示cat中的say函数

  9. 抽象(abstract)

    • 从某一个层次去看
    • 忽略细节
  10. 最近原则:本地变量的优先级高于全局变量

  11. 字段(field):类中的成员变量

  12. c++复杂的体现

    • 三种内存模型
      • 堆栈(局部)
      • 堆(动态)
      • 全局变量空间
    • 三种内存访问方式
      • 直接
      • 指针
      • 引用
  13. 所有oop语言都是默认动态绑定的,只有c++是静态绑定的:为了效率

  14. 相对关系的名词

    • 声明(declaration)和定义(definition)
    • 初始化(initialization)和赋值(assignment)
  15. 一个类下应该自己重写的函数

    • 构造函数
    • virtual析构函数
    • 拷贝构造函数
  16. 全局变量初始化

    • 对于同一个文件来说是有序的
    • 对于不同文件来说是无序的
  17. assert:如果一个指令是错误的,则退出

  18. 在计算机中set为置1,reset为置0

1|1(二)、编译过程


源文件

为了使中间文件变得简单,这里不用iostream头文件

  1. main.cpp

    #include "TicketMachine.h" //在main函数中以双引号引用 int main() { TicketMachine ticketMachine; ticketMachine.showBalance(); return 0; }
  2. TicketMachine.h

    // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
  3. TicketMachine.cpp

    // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" void TicketMachine::showBalance() { //不需要重新构建一个类 } TicketMachine::TicketMachine() { }

尝试编译并生成中间文件

使用g++ main.cpp -saves-temp 保存中间文件

  1. 预编译

    对应文件:TicketMachine.ii

    # 1 "TicketMachine.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "TicketMachine.cpp" # 1 "TicketMachine.h" 1 # 10 "TicketMachine.h" class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; # 6 "TicketMachine.cpp" 2 using namespace std; void TicketMachine::showBalance() { } TicketMachine::TicketMachine() { }

    程序:预处理器

    作用:将TicketMachine.h文件插入TicketMakchine.cpp文件

  2. 编译

    对应文件:TicketMachine.o

    .file "TicketMachine.cpp" .text .align 2 .globl _ZN13TicketMachine11showBalanceEv .def _ZN13TicketMachine11showBalanceEv; .scl 2; .type 32; .endef .seh_proc _ZN13TicketMachine11showBalanceEv _ZN13TicketMachine11showBalanceEv: .LFB0: pushq %rbp .seh_pushreg %rbp movq %rsp, %rbp .seh_setframe %rbp, 0 .seh_endprologue movq %rcx, 16(%rbp) nop popq %rbp ret .seh_endproc .align 2 .globl _ZN13TicketMachineC2Ev .def _ZN13TicketMachineC2Ev; .scl 2; .type 32; .endef .seh_proc _ZN13TicketMachineC2Ev _ZN13TicketMachineC2Ev: .LFB2: pushq %rbp .seh_pushreg %rbp movq %rsp, %rbp .seh_setframe %rbp, 0 .seh_endprologue movq %rcx, 16(%rbp) movq 16(%rbp), %rax movl $0, (%rax) nop popq %rbp ret .seh_endproc .globl _ZN13TicketMachineC1Ev .def _ZN13TicketMachineC1Ev; .scl 2; .type 32; .endef .set _ZN13TicketMachineC1Ev,_ZN13TicketMachineC2Ev .ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"

    软件:编译器

    作用:将c++代码编译为更底层的汇编语句文件

    注:

    • 编译是以一个.cpp文件为单位进行编译的
  3. 汇编

    对应文件:TicketMachine.o(二进制)

    软件:汇编器

    作用:翻译为最底层的机器码

  4. 链接

    对应文件:main.exe(windows下)

    软件:链接器(ld)

    作用:将多个单元文件(.o)与库文件整合为可执行文件

    注:对于头文件的处理(.h)尽在预编译阶段,但是与之对应的.cpp文件却可以是在链接阶段.换句话说,我们不是根据.h文件与对应的.cpp文件没有必然的联系

    示例:

    • a.h

      void f(int i,int j=0);
    • a.cpp

      #include "a.h" #include <iostream> using namespace std; void f(int i,int j){ cout << i << " " << j << endl; }
    • main.cpp

      void f(int i,int j=10); int main(){ f(10); for(;;); return 0; }
    • 结果:10 10

    • 结论:

      • 尽管main.cpp中没有a.h,最后通过链接(g++ -o test main.cpp a.cpp)使用a.cpp中的f()定义
      • 单元a中尽管用的是a的头文件,最后使用的却是main中的f声明(输出结果为10 10而不是10 0),说明传入的默认参数是在编译时确定的,而不是运行时(具体说来,函数还是只需要两个参数,而这两个参数在编译时确定了。不是说多了两个函数,否则结果应该是0 10)

1|2(三)、定义与声明


  1. 申明(declaration)与定义(definition)

  2. c++所有声明都会添加一个下划线:如global经过编译后变成了_global

  3. 声明的几种方式

    • extern变量
    • 函数声明
    • 类与结构体的声明
  4. extern关键字:表明该变量在其它模块(相当于在当前模块留下了一个"洞")

    • TicketMachine.h

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H extern int temp; //对外声明 class TicketMachine { //类声明 private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 int temp; //定义 void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; cout << "temp:" << temp << endl; } TicketMachine::TicketMachine() { }

    为什么不能在.h文件放普遍变量定义(如int temp=9)?.h文件本身就是放声明的

    例外:const int temp=9,因为常量本身就是全局的

1|3(四)、oop三大特性


封装

继承

  1. 目的:实现软件重用(还包括其它方式,如对象组合)

多态(Polymorphism)

  1. 基本实现

    • 上型
    • 动态绑定:在运行时确定调用哪个函数(搭配virtual关键字使用)
  2. c++有重载但没有覆写(即子类的函数会使父类的同名参数无效化) => c++可以通过virtual实现覆写

  3. 上型:父类属性是子类属性的子集,故子类对象可以作为父类对象调用

    内存空间:父类属性在前,子类属性在后,子类与父类的公共函数分开存放

  4. 强制转换和造型

    • 强制转换:改变了内存数据
    • 造型:改变看待一个对象的角度(从子类的角度=>从父类的角度)

1|4(五)、命名空间


  1. 类全名:std::ostream

    即std命名空间下的类

2|0二、类与头文件


2|1(一)、基本格式


  1. 新建一个TicketMachine类

    • .h文件

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H //.h文件不需要#include自己 #define TICKETMACHINE_TICKETMACHINE_H class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • .cpp文件

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 void TicketMachine::showBalance() { //不需要重新构建一个类,直接定义 cout << balance << endl; } TicketMachine::TicketMachine() { }
    • main文件

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 int main() { TicketMachine ticketMachine; ticketMachine.showBalance(); return 0; }
  2. 头文件形式

    • #incldue "xx.h":在当前目录找头文件
    • #include <xx.h>:在系统指定目录找头文件
    • #include <xx>:same as #include <xx.h>
  3. #ifndf#define

    • #ifndf XXX:如果没有XXX
    • #define XXX:那么定义XXX,知道后面的#endif为止

    尝试修改:

    • 在.h文件添加#define

      • .h文件

        // // Created by Arno_vc on 2022/3/1. // #define TICKETMACHINE_TICKETMACHINE_H #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H extern int temp; class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
      • .cpp文件

        // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" //#include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 int temp; void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; cout << "temp:" << temp << endl; } TicketMachine::TicketMachine() { }
      • 生成.ii

        # 1 "TicketMachine.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "TicketMachine.cpp" # 1 "TicketMachine.h" 1 # 6 "TicketMachine.cpp" 2 using namespace std; int temp; void TicketMachine::showBalance() { cout << balance << endl; cout << "temp:" << temp << endl; } TicketMachine::TicketMachine() { }
      • 注:可以看到没有类定义

    • 在.cpp文件添加#define

      • .h文件

        // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H extern int temp; class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
      • .cpp文件

        // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" //#include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 int temp; void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; cout << "temp:" << temp << endl; } TicketMachine::TicketMachine() { }
      • 生成ii

        # 1 "TicketMachine.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "TicketMachine.cpp" # 1 "TicketMachine.h" 1 # 7 "TicketMachine.cpp" 2 using namespace std; int temp; void TicketMachine::showBalance() { cout << balance << endl; cout << "temp:" << temp << endl; } TicketMachine::TicketMachine() { }
      • 注:可以看到没有类定义

    • 总结:由于.cpp文件的预编译是自上而下读取的,那么相同头文件时,就不会再读取,避免了出现重复定义等错误(需要注意到.cpp文件间的相互引用可以是比较复杂的关系)

      例:main.cpp引用了TicketMachine2.h和TicketMachine2.h,而TicketMachine2.h中实际也引用了TicketMachine.h

      • main.cpp

        #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 #include "TicketMachine2.h" int main() { TicketMachine ticketMachine; ticketMachine.showBalance(); return 0; }
      • TicketMachine.cpp

        // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H extern int temp; class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
      • TicketMachine2.cpp

        // // Created by Arno_vc on 2022/3/2. // #ifndef TICKETMACHINE_TICKETMACHINE2_H #define TICKETMACHINE_TICKETMACHINE2_H #include "TicketMachine.h" class TicketMachine2 { }; #endif //TICKETMACHINE_TICKETMACHINE2_H

      结果:可以正常运行

      对照(删去#ifndef):编译失败,且在main.ii文件下可以看到TicketMachine的重复引入

      main.ii

      # 1 "main.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "main.cpp" # 1 "TicketMachine.h" 1 extern int temp; class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; # 3 "main.cpp" 2 # 1 "TicketMachine2.h" 1 # 1 "TicketMachine.h" 1 extern int temp; class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); }; # 9 "TicketMachine2.h" 2 class TicketMachine2 { }; # 4 "main.cpp" 2 int main() { TicketMachine ticketMachine; ticketMachine.showBalance(); return 0; }

2|2(二)、类中的字段与函数


  1. 类中的字段也是声明的一部分(声明表示有这么个东西,定义表示这个东西在哪里)

  2. 字段是属于对象的,函数是属于类的

    类的函数调用的参数就是类本身

    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; int main() { TicketMachine ticketMachine; printf("&ticketMachine.balance:%p\n",&(ticketMachine.balance)); ticketMachine.showBalance(); return 0; }
    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <stdio.h> #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; printf("&balance:%p\n",&balance); //%p输出内存地址 } TicketMachine::TicketMachine() { }
    • 运行结果

      &ticketMachine.balance:0000006c383ff748 0 &balance:0000006c383ff748

    深入理解:每个类的函数都有this这个本地变量,作为指针指向调用函数的对象

    转换成c代码思考:f(&ticketMachine)

  3. this:类的成员变量,作为指针指向调用函数的对象

2|3(三)、构造与析构函数


  1. 构造函数的特点

    • 构造函数的函数名就是类名
    • 构造函数无返回值
  2. 对象的初始化

    • 空间是在进入{...}后开始分配的
    • 调用构造函数是在执行到相应语句
  3. default constructor与auto default constructor

    • default constructor:没有参数的构造函数
    • auto default constructor:有编译器自动生成的没有参数的构造函数
  4. 析构函数

    格式:~ClassName(){}

    使用的两种情况:

    • 被动释放:生命周期结束(以{}分隔)
    • 主动释放:delete关键字

3|0三、动态内存分配


  1. new

    本质;运算符(返回值为地址)

    作用:

    • 分配内存
    • 调用构造函数
  2. delete

    作用:

    • 调用析构函数
    • 收回内存
  3. 对应的堆中的内存分配

  4. delete []className:调用一组析构(与new className[]对应)

    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <stdio.h> #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; printf("&balance:%p\n",&balance); //%p输出内存地址 } TicketMachine::TicketMachine() { } void TicketMachine::setBalance(int balance) { this->balance = balance; //使用this关键字 } TicketMachine::~TicketMachine() { cout << "balance:" << balance << endl; }
    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; int main() { TicketMachine *ticketMachine = new TicketMachine[10]; //注意这里变量类型为指针 for (int i = 0; i < 10; ++i) { ticketMachine[i].setBalance(i); } delete [] ticketMachine; return 0; }
    • 结果

      balance:9 balance:8 balance:7 balance:6 balance:5 balance:4 balance:3 balance:2 balance:1 balance:0

      逆序释放空间

4|0四、访问属性


  1. 常用修饰符

    • private
    • public
    • protected:自身及子孙可以访问
  2. private等修饰符实际上只在编译时刻起作用

    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 #include "TicketMachine2.h" using namespace std; int main() { TicketMachine *ticketMachine = new TicketMachine(); //注意这里变量类型为指针 TicketMachine *ticketMachine1 = new TicketMachine(); ticketMachine1->setBalance(10); cout << ticketMachine->getBalance(ticketMachine1); return 0; }
    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include "TicketMachine2.h" #include <stdio.h> #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 void TicketMachine::showBalance() { //不需要重新构建一个类 cout << balance << endl; printf("&balance:%p\n",&balance); //%p输出内存地址 } TicketMachine::TicketMachine() { } void TicketMachine::setBalance(int balance) { this->balance = balance; //使用this关键字 } int TicketMachine::getBalance(TicketMachine *ticketMachine) { return ticketMachine->balance; } int TicketMachine::getBalance2(TicketMachine2 *ticketMachine2) { return ticketMachine2->balance; //无法用一个对象直接访问另一个对象的私有变量,编译都通不过 } TicketMachine::~TicketMachine() { cout << "balance:" << balance << endl; }
    • 结论

      • 在运行时,一个对象可以读取另一个同类对象的private属性(因为oop的访问修饰符特性只在编译时体现)
      • 在运行时,一个对象不可读取另一个非同类对象的private属性(在编译时就无法通过)
  3. friends(友元):友元函数可以访问类的private属性

    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H #include "TicketMachine2.h" class TicketMachine { private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); void setBalance(int balance); int getBalance(TicketMachine *ticketMachine); int getBalance2(TicketMachine2 *ticketMachine2); friend void printBalance(TicketMachine ticketMachine); //友元函数 ~TicketMachine(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 #include "TicketMachine2.h" using namespace std; void printBalance(TicketMachine ticketMachine){ cout << "printBalance:" << ticketMachine.balance << endl; //是友元函数,可以直接访问private属性 } /* void printBalance2(TicketMachine ticketMachine){ cout << "printBalance2:" << ticketMachine.balance << endl; //不是友元函数,无法直接访问private属性 } */ int main() { TicketMachine *ticketMachine = new TicketMachine(); //注意这里变量类型为指针 ticketMachine->setBalance(10); printBalance(*ticketMachine); //printBalance2(*ticketMachine); return 0; }
    • 结论正确

  4. class与struct的区别:

    • class的属性默认修饰符为private
    • struct的属性默认修饰符为public

    测试:

    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H #include "TicketMachine2.h" class TicketMachine { int temp; //无访问修饰符 private: const int PRICE=0; int balance; int total; public: TicketMachine(); void showPrompt(); void getMoney(); void printTicket(); void showBalance(); void printError(); void setBalance(int balance); int getBalance(TicketMachine *ticketMachine); int getBalance2(TicketMachine2 *ticketMachine2); ~TicketMachine(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 #include "TicketMachine2.h" using namespace std; struct Node{ int a; }; int main() { TicketMachine *ticketMachine = new TicketMachine(); //注意这里变量类型为指针 ticketMachine->setBalance(10); printBalance(*ticketMachine); //printBalance2(*ticketMachine); cout << ticketMachine.temp << endl; //私有默认属性,无法直接访问 struct Node b; cout << b.a << endl; //公有默认属性,可以直接访问 return 0; }

5|0五、初始化


5|1(一)、初始化参数列表


  1. 使用初始化参数列表

    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <stdio.h> #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 TicketMachine::TicketMachine(int balance, string name):balance(balance), account(name){ //使用初始化参数表,可以直接调用Account(string name) } TicketMachine::TicketMachine(int balance,int total,string name){ //使用构造函数初始化,对应的Account必须要有default Account,否早accout无法完成初始化 this->balance = balance; this->total = total; this->account.setName(name); } void TicketMachine::setBalance(int balance) { this->balance = balance; //使用this关键字 } int TicketMachine::getBalance() { return this->balance; } void TicketMachine::show(){ cout << "balance:" << balance << " total:" << total << " name:" << account.getName() << endl; }; TicketMachine::~TicketMachine() { }
    • Acount.cpp

      // // Created by Arno_vc on 2022/3/3. // #include "Account.h" #include <string> Account::Account() {} //用于构造函数初始化 Account::Account(string name):name(name) {} //用于参数初始化列表初始化 string Account::getName() { return this->name; } void Account::setName(string name) { this->name = name; }
    • main.cpp

      #include <iostream> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; int main() { TicketMachine *ticketMachine = new TicketMachine(1,"test"); TicketMachine *ticketMachine1 = new TicketMachine(1,2,"test"); ticketMachine->show(); //使用参数化列表 ticketMachine1->show(); //使用构造函数 return 0; }
    • 结果

      balance:1 total:512 name:test balance:1 total:2 name:test
    • 结论

      • 参数化列表:初始化
      • 构造函数:初始 => 赋值
  2. 任何类里面的成员变量都应该initialized里面做初始化,而不应该在构造函数中做初始化(否则需要default constructor)

    使用构造函数初始化有时需要default constructor

6|0六、对象组合与继承


对象组合和继承都是实现软件重用的方式

6|1(一)、对象组合


  1. 两种使用方式
    • 包含
    • 引用(指针)

6|2(二)、继承


  1. 格式

    class parent: public child{ //修饰符可以换 }
  2. protected:父类留给子类的接口用于访问private

  3. 子类与父类的构造与析构先后

    • 父类先构造,子类后构造
    • 子类先析构,父类后析构

    代码:

    #include <iostream> #include <stdio.h> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; class A{ public: A(){ cout << " A creator" << endl; } ~A(){ cout << " A delete" << endl; } }; class B : public A{ public: B(){ cout << " B creator" << endl; } ~B(){ cout << " B delete" << endl; } }; int main() { B b; return 0; }

    运行结果:

    A creator B creator B delete A delete

7|0七、函数


7|1(一)、函数重载


  1. c++的类中只有重载(overload)没有覆写(overwrite)

  2. 当子类有父类的同名函数,父类的同名参数(包括重载的)将全部被屏蔽

    代码

    #include <iostream> #include <stdio.h> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; class A{ public: A(){ cout << " A creator" << endl; } void print(){ cout << "A print" << endl; } void print(int i){ cout << "A print(i)" << endl; } ~A(){ cout << " A delete" << endl; } }; class B : public A{ public: B(){ cout << " B creator" << endl; } void print(){ cout << "B print" << endl; } ~B(){ cout << " B delete" << endl; } }; int main() { A a; B b; a.print(); a.print(1); b.print(); b.print(1); //错误 return 0; }

7|2(二)、函数默认参数


  1. 默认参数值:只有右边的参数可以省略掉

    格式:

    • TIcketMachine.h

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H #include "Account.h" class TicketMachine { private: int balance; int total; public: TicketMachine(); void setBalance(int balance,int total = 9); //default argument尽在.h文件添加 int getBalance(); void show(); ~TicketMachine(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <stdio.h> #include <iostream> using namespace std; TicketMachine::TicketMachine() {} void TicketMachine::setBalance(int balance,int total) { this->balance = balance; this->total = total; } int TicketMachine::getBalance() { return this->balance; } void TicketMachine::show(){ cout << "balance:" << balance << " total:" << total << endl; }; TicketMachine::~TicketMachine() { }
    • main.cpp

      #include <iostream> #include <stdio.h> #include "TicketMachine.h" using namespace std; void f(int i,int j=10){}; int main() { TicketMachine ticketMachine; ticketMachine.setBalance(1); ticketMachine.show(); return 0; }
  2. default argument是在编译时起作用,而不是运行时

    • a.h

      void f(int i,int j=0);
    • a.cpp

      #include "a.h" #include <iostream> using namespace std; void f(int i,int j){ cout << i << " " << j << endl; }
    • main.cpp

      void f(int i,int j=10); int main(){ f(10); for(;;); return 0; }
    • 结果:10 10

    • 说明:

      • 若默认参数是多了重载函数:则这些函数的第二个参数必然为0,因为main.cpp与a.cpp仅在链接步骤出现关联,而链接步骤只做了链接.
      • 默认参数显然只能在编译时补上:由上显然只是一个函数然后适时补上了参数,都运行了还怎么补,那显然只能在编译时补(具体函数调用过程见下)
  3. 少用default value,以免阅读障碍等问题

7|3(三)、内联函数


  1. 作用:在编译时将对应的函数嵌入在源代码中(汇编代码阶段,以空间换时间)

  2. 优点

    • 自身优点:避免了原函数需要的大量堆栈操作
    • 相较于宏定义:编译器无法对宏定义做类型检查,但可以对内联函数做检查
  3. 缺点:当函数代码过长或者使用了递归,编译器不会使用inline

  4. 调用格式:与default arguments不同,内联函数inline只需要在.h文件

    • 使用内联

      • main.cpp

        #include "a.h" int main(){ A a; a.f(1,2); for(;;); return 0; }
      • a.h

        class A{ public: inline int f(int i,int j=0); ////inline function definition }; int inline A::f(int i,int j){ //inline function definition return i+j; }
      • 观察 汇编文件main.s,共49行

    • 不使用内联

      • main.cpp

        #include "a.h" int main(){ A a; a.f(1,2); for(;;); return 0; }
      • a.h

        class A{ public: int f(int i,int j=0); ////inline function definition };
      • a.cpp

        #include "a.h" int A::f(int i,int j){ //inline function definition return i+j; }
      • 观察汇编文件main.s,a.s,共27+24行(对于循环的调用应该效果会更明显吧)

  5. 类声明中的函数都是inline的,但通常更喜欢将需要inline的函数定义放在类外

8|0八、const关键字


  1. extern const int bufsize

    • 外部有一个变量bufsize,这个bufsize是不是const无所谓
    • 在我这里不能修改
  2. 几种常见使用错误

    • const对应的变量为什么无法用来初始化数组

      #include <iostream> using namespace std; int main(){ int x; cin >> x; const int y; int arr[y]; return 0; }

      原因:函数的局部变量包括数组是存放在堆栈中的,在编译时由编译器分配对应的内存空间。当y是变量时,显然编译器无法分配数组需要的栈空间报错。

    • const变量的地址无法赋值给普通指针

      #include <iostream> using namespace std; int main(){ int const x=3; int *p; *p=&x; return 0; }

      原因:x认为不能修改,但p认为可以修改,两者地位平等,存在逻辑冲突

  3. 指针用法

    • char * const q = "abc":指针无法修改
    • const char *p = "abc":无法通过改指针修改内容

    注:const在*后,对象是const;const在*后,指针是const

  4. 变量存放的三个区域:

    • 全局变量:全局数据区(其中常量在代码段中)
    • 本地变量:堆栈(由编译器分配)
    • 动态分配的内存:堆
  5. const char *s = "Hello world!"char s[] = "Hello world!"

    • main.cpp

      #include <iostream> #include <stdio.h> //#include "TicketMachine.h" using namespace std; int main() { const char* s1 = "Hello world!"; //指向代码段 char s2[] = "Hello world!"; //指向堆栈 printf("main:%p\n",main); printf("s1 :%p\n",s1); printf("s2 :%p\n",s2); return 0; }
    • 结果

      main:00007ff64e541731 s1 :00007ff64e54b001 s2 :000000c3957ff94b
    • 结论

      • 前者仅仅是将指针指向代码段(无法修改)
      • 后者是在堆栈中分配了内存空间并赋值
  6. 函数参数为什么要传const指针

    原因:

    • 传入对象要在对栈中开辟一个临时的数据空间,并放入数据
    • 传入指针可能会被修改,不安全\

    结论:

    • 传入const指针既能方便传入对象,又能保证不被修改
  7. const对象:对象中的值都是无法修改的 => 在函数后加const保证对象中的函数不会修改成员变量(声明和定义后都要加const,实际上表示this要加const)

    • TIcketMachine.h

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H #include "Account.h" void f(int i,int j=0); class TicketMachine { private: int balance; int total; public: TicketMachine(); int getBalance() const; //对函数声明进行const void show(); ~TicketMachine(); }; #endif //TICKETMACHINE_TICKETMACHINE_H
    • TicketMachine.cpp

      // // Created by Arno_vc on 2022/3/1. // #include "TicketMachine.h" #include <stdio.h> #include <iostream> //在cpp下加额外需要用的头文件 using namespace std; //需要加标准命名空间 //void f(int i,int j){ // cout << i << " " << j << endl; //} TicketMachine::TicketMachine() {} int TicketMachine::getBalance() const { //对函数定义加const return this->balance; } void TicketMachine::show(){ cout << "balance:" << balance << " total:" << total << endl; }; TicketMachine::~TicketMachine() { }
    • main.cpp

      #include <iostream> #include <stdio.h> #include "TicketMachine.h" using namespace std; int main() { const TicketMachine ticketMachine; //const对象 cout << ticketMachine.getBalance() << endl; //可以运行 ticketMachine.show(); //由于我们在声明ticketMachine时是const, 而show()不是const函数(编译器认为它不一定是安全的),故无法调用 return 0; }
  8. const构成重载

    void f(); //A* this void f() const; //const A* this

9|0九、引用


  1. 基本定义:引用可以看做是别名

    本质:用const *实现

  2. 与const

    const int& z=x; //z的应用对象本身不能改变的,这里const表示不能通过z修改x(类比*)
  3. 与*

    • int *& p;:一个int *类型的引用
    • int &* p:非法
  4. 使用:函数参数

    void f(int& x); //注意这个是函数声明,以往见到的多是调用中加&符号
  5. 无法做函数重载

  6. 没有reference的reference

    int x; int &a=x; int &b=a; //error

10|0十、virtual关键字


  1. 使用virtual关键字的类第一个int字节不再是普通的属性,而是指针,指向vtable(虚拟函数表)

    • main.cpp

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } }; typedef long long ll; int main() { //int 4,long 4,long long 8 A a; A b; ll *p = (ll*) &a; cout << *(ll*)p << " i:" << *(int *)(++p) << endl; //8个字节*8=64位地址 p = (ll *) &b; b.i=20; cout << *(ll*)p << " i:" << *(int *)(++p) << endl; ll *temp = (ll*)&b; cout << *(ll*)(*temp) << endl; //vtable地址 return 0; }
    • 结果

      140695808005440 i:10 140695808005440 i:20 140695807996512
    • 结论:在使用virtual情况下,同一个类的首地址指向vtable(虚拟函数表)

  2. 子类与父类的vtable位置不同

    • main.cpp

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } }; class B:public A{ public: virtual void f(){ cout << "B" << endl; } }; typedef long long ll; int main() { //int 4,long 4,long long 8 A a; B b; ll *p = (ll*) &a; cout << *(ll*)p << " i:" << *(int *)(++p) << endl; //8个字节*8=64位地址 p = (ll *) &b; b.i=20; cout << *(ll*)p << " i:" << *(int *)(++p) << endl; return 0; }
    • 结果

      140696722494832 i:10 140696722494864 i:20
    • 结论:父子类的vtable地址不同(哪怕父类没有覆写过虚函数)

  3. 类的赋值与指针的赋值

    • main.cpp

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } }; class B:public A{ public: virtual void f(){ cout << "B" << endl; } }; typedef long long ll; int main() { //int 4,long 4,long long 8 B b; A a1 = b; //赋值 a1.f(); A *a2 = &b; //指针 a2->f(); return 0; }
    • 结果

      A B
    • 结论:不言自明

  4. 如果一个类里面有一个析构函数,那么析构函数也应该是virtual的

    • main.c

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } ~A(){ cout << "delete A" << endl; } }; class B:public A{ public: virtual void f(){ cout << "B" << endl; } ~B(){ cout << "delete B" << endl; } }; typedef long long ll; int main() { //int 4,long 4,long long 8 A *a = (A *)new B; delete a; return 0; }
    • 结果

      delete A
    • 结论:由于普通函数的静态链接导致这里调用的A类的析构函数(而不是本身的B类)

    • main.c修改为virtual函数

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } virtual ~A(){ cout << "delete A" << endl; } }; class B:public A{ public: virtual void f(){ cout << "B" << endl; } virtual ~B(){ cout << "delete B" << endl; } }; typedef long long ll; int main() { //int 4,long 4,long long 8 A *a = (A *)new B; delete a; return 0; }
    • 结果

      delete B delete A
    • 结论:成功自下而上调用析构函数

  5. 通过FatherClass::func()调用父类函数

    • main.c

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } virtual ~A(){ cout << "delete A" << endl; } }; class B:public A{ public: virtual void f(){ A::f(); } virtual ~B(){ cout << "delete B" << endl; } }; typedef long long ll; int main() { A *a = (A *)new B; a->f(); return 0; }
    • 结果

      A
    • 结论:子类用父类民调用父类函数

  6. 通过virtual实现override

    • main.cpp

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: int i; public: A():i(10){}; virtual void f(){ cout << "A" << endl; } virtual void f(int para){ cout << "A " << para << endl; } virtual ~A(){ cout << "delete A" << endl; } }; class B:public A{ public: virtual void f(){ //必须全部用virtual cout << "B" << endl; } virtual void f(int para){ //必须全部用virtual cout << "B " << para << endl; } virtual ~B(){ cout << "delete B" << endl; } }; typedef long long ll; int main() { A *a = (A *)new B; a->f(); return 0; }
    • 结果

      B

11|0十一、拷贝构造


  1. 特点

    • 没有调用普通构造函数(与普通的new比较)
    • 发生的不是字节到字节的拷贝,而是member到member的拷贝
  2. 基本格式:T::T(const T&); //由c++给出,可以自己覆盖

    • 函数参数

      • main.cpp

        #include <iostream> #include <stdio.h> using namespace std; int objectNum=0; class A{ public: A(){ objectNum++; cout << "objectNum: " << objectNum << endl; } ~A(){ objectNum--; cout << "objectNum: " << objectNum << endl; } }; A f(A a){ cout << "in function, objectNum:" << objectNum << endl; return a; } int main() { A a; cout << "before function" << endl; A b = f(a); cout << "after function" << endl; return 0; }
      • 结果

        objectNum: 1 before function in function, objectNum:1 objectNum: 0 objectNum: -1 after function objectNum: -2
      • 结论:f中的参数和返回值所使用的都没有走构造函数,而是通过其它方式(拷贝构造)

    • 基本使用

      • main.cpp

        #include <iostream> #include <stdio.h> using namespace std; int objectNum=0; class A{ public: A(int temp){ objectNum++; cout << "objectNum: " << objectNum << endl; } ~A(){ objectNum--; printf("%p ",this); cout << "objectNum: " << objectNum << endl; } }; int main() { A a(1); A b = 2; return 0; }
      • 结果

        objectNum: 1 objectNum: 2 000000d4e11ff84e objectNum: 1 000000d4e11ff84f objectNum: 0
      • 结论:A a = 1; //等效与A a(2)

    • 覆写

      • main.cpp

        #include <iostream> #include <stdio.h> using namespace std; int objectNum=0; class A{ public: A(){ objectNum++; cout << "objectNum: " << objectNum << endl; } A(const A &b){ objectNum++; //A *temp = &b; printf("&b :%p this:%p\n",&b,this); //被拷贝的对象地址,原对象地址 cout << "objectNum: " << objectNum << endl; } ~A(){ objectNum--; printf("%p ",this); cout << "objectNum: " << objectNum << endl; } }; A f(A a){ printf("para a :%p\n",&a); //参数a cout << "in function, objectNum:" << objectNum << endl; return a; } int main() { A a; printf("initial a:%p\n",&a); //初始a cout << "before function" << endl; A b = f(a); //但是f(a)也会是相同的效果 printf("b:%p\n",&b); return 0; }
      • 结果

        objectNum: 1 initial a:00000052915ff86e before function &b :00000052915ff86e this:00000052915ff86f // a=>para a objectNum: 2 para a :00000052915ff86f in function, objectNum:2 &b :00000052915ff86f this:00000052915ff86d // para a => b objectNum: 3 00000052915ff86f objectNum: 2 b:00000052915ff86d 00000052915ff86d objectNum: 1 00000052915ff86e objectNum: 0

    问题:f(a)是一个拷贝构造,那么A b=f(a)为什么也只有一个拷贝构造?

    • main.cpp

      #include <iostream> #include <stdio.h> using namespace std; class A{ public: A(){ } A(const A &b){ printf("&b :%p this:%p\n",&b,this); //被拷贝的对象地址,原对象地址 } }; A f(A a){ return a; } int main() { A a; f(a); cout << "--------------" << endl; A c = f(a); printf("c:%p",&c); return 0; }
    • 结果

      &b :0000007e19fff90c this:0000007e19fff90e &b :0000007e19fff90e this:0000007e19fff90d -------------- &b :0000007e19fff90c this:0000007e19fff90f &b :0000007e19fff90f this:0000007e19fff90b c:0000007e19fff90b
    • 结论:根据翁凯老师的讲解,这里编译器做了个优化(即要不要return回一个临时对象)

      • 原形式

        f(){ A b; return b; } Person @temp = f(a); A c = @temp;
      • 编译器优化后的形式

        f(){ A b; return b; } A c = b; //优化了一次拷贝构造
      • main.cpp验证

        #include <iostream> #include <stdio.h> using namespace std; class A{ public: A(int t){ } void show(){ cout << "show" << endl; } A(const A &b){ printf("&b :%p this:%p\n",&b,this); //被拷贝的对象地址,原对象地址 } }; A f(int t){ A a(t); //非拷贝构造 printf("a:%p\n",&a); return a; } int main() { A c = f(2); //没有发生拷贝构造 printf("c:%p",&c); return 0; }
      • 结果

        a:00000043cabff98f c:00000043cabff98f
      • 结论:没有使用拷贝构造(被优化了)

  3. 调用拷贝构造的几种情况

    • 对象作为函数的参数传入
    • 用一个对象初始化另一个对象
  4. 拷贝构造对指针属性的影响:将指针指向了同一个位置 => 然而许多时候我们并不希望这样

    • 注意到c++中的string有拷贝构造,而c中的char *没有,因此更推荐用string
    • 一般情况下建议自己写拷贝构造
  5. 禁止拷贝构造:将拷贝构造函数private

  6. 拷贝构造与初始化的不同

    • 拷贝构造:调用拷贝构造函数
    • 初始化:调用赋值运算符

12|0十二、static关键字


  1. static在c中的使用

    • 本地变量(hidden):将变量(函数)限制在当前文件使用
    • 持久化存储(persistent):而不是在某个函数调用后就收回
  2. static在c++中的使用

    • 修饰类:存储是全局的(链接时分配),但初始化依然是在函数内部,只初始化一次
    • 修饰属性:属于类的静态成员属性和静态成员函数(这里表现的是其persistent特性,他的hidden特性由private替代了)
  3. 类中的static属性的初始化

    • 需要在.cpp中初始化

      • main.cpp

        #include <iostream> #include <stdio.h> using namespace std; class A{ public: static int i; //.h:声明 }; int A::i; //.cpp:定义,不能在前面添加static,因为static属性可以在.cpp外文件访问 int main() { cout << A::i << endl; return 0; }
      • 结果

        0
    • 不能通过初始化参数列表初始化

    • 在内部通过this访问

    • 在外部通过ClassName::field访问

  4. 静态成员函数调用静态参数

13|0十三、运算符重载


  1. 限制

    • 只能对已有运算符重载
    • 重载必须对一个类或枚举类型
    • 重载必须保持原来的参数个数与优先级
  2. 通过operator关键字重载

  3. 运算符重载类型

    • 全局函数:完整写出所有参数

    • 成员函数:使用reciver作为实际调用运算符重载函数的对象(在全局的重载函数中是第一个参数)

      a + b为例,实际可以写作a.operator+(b),故reciver是左边的a

      应该选择作为成员函数的情况:

      • 单目运算符
      • 常见的如:=,(),->,->*
  4. 基本概念

    • return value

      • 修改当前对象
      • 返回新对象
        • 新对象可以被修改
        • 新对象不能被修改
    • 常见操作符原型

      • +-*/%^&|~

        const T operatorX(const T& l,const T& r) const;
      • ! && || < <= == >= >

        bool operatorX(const T& l,const T& r) const;
      • []

        T& T::operator[](int index);
      • ++ --

        const Integer& operator++(); //原值+1 const Integer operator++(int); //postfix,返回新的同值对象,原值+1 const Integer& operator--(); const Integer operator--(int); //postfix

        注:通过int,编译器能够识别那个是prefix哪个是postfix

      • =

        T& T::operator=(const T& rhs){ if(this!=&rhs){ //perform assignment } return *this; }
      • 强制转换

        • 通过构造函数转换

          • 隐式转换

            main.cpp

            #include <iostream> #include <stdio.h> using namespace std; class One{ public: One(){}; }; class Two{ public: Two(const One&){} }; void f(Two t){ } int main() { One one; f(one); //隐式转换:给的是One,但是能自动转换为Tow return 0; }
          • 显示转换

            main.cpp

            #include <iostream> #include <stdio.h> using namespace std; class One{ public: One(){}; }; class Two{ public: explicit Two(const One&){} //加入关键字构成显示转换 }; void f(Two t){ } int main() { One one; f(Two(one)); //显示转换:此时编译无法通过 return 0; }
        • 使用强制转换(少用)

          X::operator double() const; //没有返回类型,因为double已经说明了将X对象转变为T

14|0十四、模板


  1. 场景:不同类型的数据需要用到相同的代码段

    选择:

    • 使用基类
    • 代码克隆(不好管理)
    • 无类型列表(void *)
  2. 函数模板

    • main.cpp

      #include <iostream> using namespace std; //声明而非定义 template <typename T> void Swap(T &a,T &b){ //注意不要用stdio.h中的swap T temp=a; a=b; b=temp; cout << "swap over" << endl; } int main() { int a=1,b=2; float c=1.0,d=2.0; cout << "a:" << a << " b:" << b << endl; Swap(a,b); cout << "a:" << a << " b:" << b << endl; cout << "c:" << c << " d:" << d << endl; Swap(c,d); cout << "c:" << c << " d:" << d << endl; return 0; }
    • 结果

      a:1 b:2 swap over a:2 b:1 c:1 d:2 swap over c:2 d:1
    • 本质

      • 读取模板函数声明(有些在.h文件)
      • 确定所需要的模板函数参数类型
      • 生成函数
  3. 类模板:类模板里面的每个函数都是函数模板

    使用:

    • main.cpp

      #include <iostream> #include "TicketMachine.h" using namespace std; int main() { TicketMachine<int> t; //放入int int a=1,b=2; cout << "a:" << a << " b:" << b << endl; t.swap(a,b); cout << "a:" << a << " b:" << b << endl; return 0; }
    • TicketMachine.h

      // // Created by Arno_vc on 2022/3/1. // #ifndef TICKETMACHINE_TICKETMACHINE_H #define TICKETMACHINE_TICKETMACHINE_H #include <iostream> using namespace std; //声明而非定义 template <class T> class TicketMachine { public: void swap(T& a,T& b); }; template <class T> //.cpp这里也要加templete void TicketMachine<T>::swap(T &a,T &b){ //类名后需要加T T temp=a; a=b; b=temp; cout << "swap over" << endl; } #endif //TICKETMACHINE_TICKETMACHINE_H
    • 结果

      a:1 b:2 swap over a:2 b:1

    注:

    • 模板类中函数的声明与定义都应该写在.h文件,否则链接错误(从编译角度可以理解,因为倘若放在另一个模块那是无法直接调用,必须将对应的函数定义放在同一个模块来真正构造函数)
    • 类模板可以有默认参数
    • 类模板可以继承

15|0十五、异常


  1. 场景:不可控因素,例如读取一个文件,而那个文件实际不存在

  2. 优点:将业务逻辑和错误处理逻辑分开

  3. 使用

    • 抛出异常,中断并上溯:throw VectorIndexError(index);

      注:

      • 这个对象不是new出来的,故不在堆栈,而是在堆里

      • throw;会直接将catch到的异常继续上抛

      • new关键字无法申请到内存,则抛出bad_alloc异常

    • 捕获异常

      try {...} catch ... catch ...

      注:

      • 参数中的...表示捕获所有异常

      • 匹配顺序(自上而下)

        • exact
        • base
        • ellipse(...)
      • 通过在函数定义后添加操作表示最多能抛出的异常

        void abc(int a):throw(MathErr){ //本人最多只能抛出MathErr,如果抛出其它则会被unexpected exception替代 ... }
      • 异常传递:建议用引用

      • 构造函数丢异常也可能造成内存泄漏

        class A{ public: A(){ buf = new char[1024]; delete this; } ~A(){ delete buf; } } int notmain(){ A a; A* p = new A(); //整个函数(以notmain函数为例)在此结束,无法通过delete p收回char[1024]的空间 delete p; return 0; }

16|0十六、流


  1. 特点:一维单方向

    • 缺点
      • 比较慢
      • 比较繁琐(内部)
    • 优点
      • 开发效率较高

    注:与之相对的fscanf等接口是随机访问的

  2. 二进制文件:不以人的阅读为目的的文件(因为从某个角度来说,所有的文件都是二进制的)

  3. c++中的流对象

    • cin
    • cout
    • cerr:错误
    • clog:日志
  4. 操纵器(manipulate):操纵器,例如:endl,flush

17|0十七、STL模板


  1. 主要内容

    • 数据结构

      常用:

      • Vector
      • Deque:expands at both end
      • List:double-linked
      • sets and maps

      特点:

      • 自动增长
      • 建议用!=.end()判断是不是走到头了
    • 算法(函数模板)

      常用:

      • sort
      • search:二分
  2. 通用的iterator


  1. 运算符通常是有结果的

  2. 可以对对象进行赋值(虽然看起来毫无意义)

    #include <iostream> #include <stdio.h> #include "TicketMachine.h" //在main函数中以双引号引用 using namespace std; class B{ public: B(int i){ } }; int main() { B b(1); //相对于B *b,B b申请了内存空间 b=0; return 0; }
  3. 尽量将代码建筑在已有的代码基础上


__EOF__

本文作者Arno
本文链接https://www.cnblogs.com/Arno-vc/p/15988122.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Arno_vc  阅读(314)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示