C++:中级语言

------------直接与计算机内部硬件交互的代码 ------------面向对象 ------------1997年在贝尔实验室开始设计开发
    

C++的特性

------------速度 ------------静态类型 ------------多范式编程语言 ------------面向对象 ------------标准库
    

C++的变量

命名规则 ------------只能由大小写字母与数字与下划线组成 ------------变量名不能以数字开头,第一个必为字母或下划线 ------------变量名区分大小写 ------------变量名不能是关键字 ------------变量名可以用下划线开头,但这并不好 -------------------------------------------------- #include< iostream > int main() { int a;//可以 int _a;//可以 int A;//可以 int 1a;//不行,不能以非字母或下划线以外的开头 int int;//不行,不能把关键字当变量名 }
    

C++的字面量

------------用来表示固定的数值 1>整数字面量(具有分数形式或指数) 2>字符字面量(单引号) 3>转义符 '\b' -退格 '\n' -换行 '\t' -水平制表 '\\' -反斜杠 '\0' -空字符 '\f' -换页 '\r' -回车 '\v' -垂直制表 '\'' -单引号 '\"' -双引号 '\?' -问号 3>字符串字面量(双引号)
    

C++常量

Const int a = 298;//声明a为常量(即不可更改的)
    

C++基本数据类型

字节 布尔 bool 1 字符 char 1 整型 int 2或4 浮点 float 4 双浮点 double 8 无类型 void 0 宽字符 wchat_t 2 -------------------------------------------------- int a = 1; float b = 64.74; double c = 134.6473; char d = 'h'; wchar_t e = L'a'//此a是阿尔法,我打不出来😭 bool f = false;
    

C++类型修饰符

------------signed ------------unsigned ------------short ------------long ------------------------------------------------------------- 字节 char 1 -128~127或0~255 unsigned char 1 0~255 signed char 1 -128~127 int 4 -2147483648~2147483647 unsigned int 4 0~4294967295 signed int 4 -2147483648~2147483647 unsigned short int 2 0~65535 signed short int 2 -32768~32757 long int 4 -9223372036854775808~9223372036854775807 signed long int 8 -9223372036854775808~9223372036854775807 unsigned long int 8 0~18446744073709551615 float 4 ~ double 8 ~ long double 16 ~ wchat_t 2或4 ~(这些不想写了,根本写不完太大了,我反正懒得写)
    

C++输出

#include< iostream > using namespace std; int main() { cout<<"你好,世界";//不是我不想打英文版的,是我不会拼啊,hollw word? return 0; } //你也看出来了,cout用于输出,并且<< 运算符你可以想象成右边的数据流,流向左边,左边的cout输出数据流 //--(如何工作) 1>包含允许输出的iostream头文件 2>cout对象在std命名空间内定义 3>每个C++程序都以main函数开始 4>cout是一个在引号“”内打印字符串的对象<< 是运算符 5>return是main函数的退出,程序以此结束,不是强制需要return的 ----------------------------------------------------------------------- ----------------------------------------------------------------------- #include< iostream > using namespace std; int main() { int num1 = 70; double num2 = 256.783; char ch = 'A'; cout<< num1<< endl; cout<< num2<< endl; cout<< "character: "<< ch<< endl; return 0; } //--(如何工作) //1>endl的作用是换行,可插入到输出流中 //2>在单语句中打印不同变量,字符串等,可以多次使用<< 运算符
    

C++输入

#include< iostream > using namespace std; int main() { int num; cout<< "输入一个整数:"; cin>>num; cout<< "这个数字是: " << num; return 0; } //------------------------] 1>如果不包含using namespace std;则需要std:: cin代替cin
    

C++接受多个输入

#include< iostream > using namespace std; int main() { char a; int num; cout<< "输入一个字符和一个整数:"; cin>> a >>num; cout<< "字符:"<< a<< endl; cout<< "数字:"<< num; return 0; } //---------------------------------------- 输入多个不同类型值时通过回车来输入
    

C++类型转换

类型转换分为: 1>隐式转换(自动转换) 2>显式转换(强制转换类型) //--------------------------------------------- 隐式:int——>double #include< iostream > int main() { int num_int = 9; double num_double; num_double = num_int; std::cout<< "num_int = "<< num_int<< std::endl; std::cout<< "num_double = "<< num_double<< std::endl; return 0; } //-------------------------------------------------- 将num_int 赋给num_double 之前编译器已将int——>double //--------------------------------------------------- 隐式:double——>int #include< iostream > int main() { int num_int; double num_double = 9.99; num_int = num_double; std::cout<< "num_int = "<< num_int << std::endl; std::cout<< "num_double = "<< num_double<< std::endl; return 0; } //------------------------------------------------------ 以下是显式: 例如: num_double = (double)num_int;//c语言里面是这样的,因此叫C型强制转换 或者 num_double = double(num_int);//这种有点像函数,C++老式风格,叫函数表示法 //--------------------------------------------------------------- 相信你看出来了,无非就是无聊的人,相互类型转换罢了,这种过程也叫“造型”, 当然不是你的头发,向上造型和向下造型的结果是不一样的,向上造型就是类似 int ——> double 数据并不会丢失 而向下造型 double ——> int 数据可能会丢失 为什么? 你怎么能问这么低级的问题? 当然是因为double包含小数,int有小数吗?
    

C++运算符

不行了,太多了,我不想打出来 不是,你complain什么啊? 我就是emotional怎么啦? 没事,武懒蛋不想打出来
    

C++运算符优先级

什么?这个也不想打? yes 反正也不会看
    

C++注释

单行 // 多行 /* */ 【你怎么又愿意打了?】 因为这个少啊
    

C++分支控制

跟C一样 1>if....else 2>while(...){...} 3>do{..}while{...};//注意了,这里是有分号的,是一个语句 4>break; continue; 作用跟C一样 switch(...)//这个就是类似判断你打游戏按WASD,到底是按哪一个键 { case ____ ://一些确定值 ....... break; ....... default:.....;//这是个默认触发语句,就是前面的都不符合的时候会执行这个 }
    

C++goto语句

goto jump; ..... jump: a = b/c; .....
    

C++函数

#include< iostream > #include< cmath > int main() { double number, squareRoot; std::cout << "输入一个数字:"; std::cin >> number; squareRoot = sqrt(number); std::cout << "数字" << number << "的平方根= " << squareRoot; return 0; } //------------------------------------------------------------------------ 1>调用sqrt()来算平方根 2>每个C++程序至少有一个函数(main) //------------------------------------------------------------------------ #include< iostream > using namespace std; int add(int, int);//函数原型(声明) int main() { int num1, num2, sum; cout<< "输入两个要相加的数字"; cin >> num1 >> num2; sum = add(num1, num2);//调用函数 cout << "总和 =" << sum; return 0; } int add(int a, int b)//函数定义 { int add; add = a + b; return add;//返回语句 }
    

C++内联函数

#include< iostream > #include< ctime > using namespace std; inline double f(double x) { return 2 * x * x - 1; } int main() { cout << f(3.0) << endl; return 0; } //inline的作用就是使f(3.0)在编译的时候 //将f(3.0)替换成f()函数的本体,也就是为什么叫内联函数了 //好处就是能够节省参数传递,控制转移等开销,提高代码执行效率 //其次内联函数应当简小,递归调用函数无法成为内联函数
    

C++函数重载

1>int test(){} 2>int test(int a){} 3>float test(double){} 4>int test(int a,double b){} 没错好好思考一下,他们的函数名都一样,为什么程序允许他们存在 当然了,难道双胞胎及以上,都只能留下一个人吗? 同名,但参数或类型不同,是允许使用同样一个函数名字的 这是因为他可以使得编译器分辨出来,他们的区别 或许你会问,这样做有什么意义?我为何不分别去取不同的名? 当然,你当然可以这样做,当是这样做是有好处的 假设你要一个傻瓜输入一个整数 但是他输入的是一个字符串 你就不能用接收整数的函数去处理它,需要一个接收字符串的函数 func(int) | ||| | func(s[...]) 编译器在注意到用户输入为字符串后自动在同名的函数中选择接收字符串的函数来处理请求
    

C++默认参数(实参)

-------------------先看下面的程序--------------------------- void temp(int = 10,float = 8.8);//函数声明 int main()//主函数 { temp(); } void temp(int i, float f)//函数定义 { ...函数体... } ----------------------END--------------------------------- 嘿,你丫的,别走神,C++和c的区别,c是不会关心你在函数声明时的操作的,c唯一关注的是,你声明了什么函数和什么类型参数 并且在你调用函数的时候如果你声明的函数有参数,而在调用的时候没有传入参数会报错 c++就不太一样,你可以在声明的时候就设置参数的默认值,就相当于,你要求一个人 输入“我是一个大傻瓜”,他拒绝并直接回车执行,这个时候,C++自动回忆你在声明的时候设置的默认值 “不输入的傻蛋”赋给函数的参数 别问有什么用,你不会用别人会,你要做的就是能够看得懂别人的代码并学习别人的思想 -------------------使用默认参数时常见的错误---------------------------------------- 1>void add(int a,int b = 3,int c,int d = 4) 你不能跳过参数设置两个参数的默认值 也就是你不能跳过c,必须为c分配默认值,除非你不想为d分配默认值 为什么?别问,just do it。 “我他妈的就是 想知道!”,好好好 你想想看这个,当一个傻瓜调用这个函数时 add(1,2) 编译器首先知道你设置了两个默认参数,a和c是没有的 你或许是想给a和c赋值,但你也可能是给a,b赋值 看到了吧,what you mean? =====”你猜“ 2>void add(int a, int b=3,int c, int d) 分配的默认值后所有的参数都需要分配 如果只需要一个默认,确定它是最后一个参数 C++ 规定函数参数的默认值必须是连续设置的,且从右向左进行设置。这样在调用函数时,省略参数时就能够明确地知道每个实参对应的形参位置,从而避免歧义。 ------------------------------------------------------------------------------ 总之,你要么全设置默认值,给程序留点喘息的机会 或者你从傻瓜入手,让他一定输入你想看到的东西(这样你就赚不到傻子的钱了😒)
    

C++存储类

---------------------------------了解就行,反正你也看不懂------------------------------ 1>auto(自动存储型) C++17开始auto不再是存储类的关键字 2>register(寄存器) C++17被弃用 3>static(静态) ——局部变量:函数调用之间保持值(看看前面是尼玛的static,别问那些奇怪又毫无理由的问题) int main() { function(); .... } int function() { static a;//a不会在func函数调用完后被销毁 ...... } ——全局变量:变量的作用域限制在声明的文件中 static b;//b是在所有函数外定义的并且声明为static,也就是所有函数都可以访问它,并且不会销毁,但是也限制了它只能在这个文件中使用 int main() { ..... } 4>extern(继承) 提供其他文件中全局变量的引用 5>mutable 仅适用于类的对象(最后讲解(不是这篇的最后)) 6>thread_local ——可与static与extern合并 ——变量仅在线程创建时存在
    

C++的类型限定符

---------------------------------必须认识,就这几个而已🤤------------------------------ 1>const 程序执行期间不得修改 2>volatile 不要优化变量,不放到寄存器中,从内存读取变量 3>restrict 由此修饰的指针是唯一一种访问它所指向的对象的方式 C99新增 ---------------------------------是不是头大了🤣--------------------------------------
    

C++返回引用

--------------------------先来看一段代码---------------------------------- #include< iostream >//输入输出流头文件 using namespace std;//这样我们就不用写重复的命名空间代码 int num;//声明一个int类型变量 int& test();//声明一个返回int引用类型的函数,在c中类似int * test(); int main()//返回int类型的主函数,卧槽了,我都不想写这种注释了,写吐了(得了吧,你也就写这种教程的时候写写,你平常写代码会写注释吗?你那代码是人看得懂的吗) { test() = 5; cout << num; return 0; } int& test() { return num; } ------------------------------------END---------------------------------- 别急,具体解释在这里: test()函数的返回类型为int& 因此返回的是int类型的引用,而num正巧是int类型 也就是说test函数返回num变量的地址 test() = 5;可以看成 num的引用 = 5; 也就是将5存储到num中 ------------------------------------------以下是一些注意事项---------------------------------------- int& test() { return 2; //不能返回常量 } ------------------------------------------------------ int& test() { int n = 2; return n;//不能返回局部变量 } 那有些东西就要假设了,我把n加上static声明可以返回吗? int& test() { static int n =2; return n; } 恭喜你,是可以的,它并没有在函数调用后被销毁,看来你不蠢啊(如果你没有想到这一点,看来还是我聪明点嘿)
    

C++数组

---------------------------------🤤------------------------------ 1>声明数组 dataType arrayName[size]; ->float arr[5]; 2>访问数组元素 ---- 通过使用索引 ------------------------- | 0 | 1 | 2 | 3 | 4 | 5 | ------------------------- 对应这种 ------------------------------------------------------- | arr[0] | arr[1] | arr[2] | arr[3] | arr[4] | arr[5] | ------------------------------------------------------- 即arr[0]; 就是0;//停止你的牛角尖,你是不是还会想0 arr[0] 这是不是有什么关系?完全没有。单纯就是我看着方便,我把数组第一个元素改为99,arr[0]就是99 3>初始化数组 在定义数组的时候就初始化 int arr[5] = {10,20,30,40,50}; 或者 int arr[] = {10,20,30,40,50};//这个编译器会自动计算初始化的元素个数自动给数组赋大小 4>C++允许函数返回数组 5>多维数组与C的定义一样,这里不再多说 ---------------------------------下面是一些举例-------------------------------------- 定义----》 int arr[5] = {10,20,30,40,50}; 将第四个元素改为9-----》 arr[3] = 9; 读取输入并将值赋给数组的第三个元素-----》 cin >> arr[2]; 将数组第三个元素打印输出-------》 cout << arr[2]; ---------------------------------是不是头又大了🤣-------------------------------------- #include < iostream > using namespace std; int main() { //数组 int array[5]; //array[0] = 2; //array[1] = 1; //array[2] = 1; //array[3] = 1; //array[4] = 1; for (int i = 0; i < 5;i++) { cout << array[i] << endl;//未初始化的数组打印的是 0xCCCCCCCC的二进制 } } ---------------------------------分割线-------------------------------------- #include < iostream > #include < vector >//可变长数组库引用 using namespace std; int main() { vector< int > array;//可变长数组声明 array.push_back(1); for (int i = 0; i < 5;i++) { array.push_back(2); cout << array[i] << endl; } cout << array[5];//程序在这里可以打印出最后一个元素 cout << array[6];//程序在这里报错,因为越界了,我们循环五次加了五个元素,加上一开始的一共六个,因为数组小标从0开始所以,这个是第七个越界了 } ---------------------------------分割线--------------------------------------
    

C++字符串

1>定义一个字符串 char str[] = "C++"; char str[4] = "C++"; char str[] = {'C','+','+','\0'}; char str[4] = {'C','+','+','\0'}; 以上效果一样 2>输入输出 char str[100]; cin>>str;//输入,输入流指向str对象 cout << str << endl;//输出,输出流指向输入 -------------------------------------- 读取一行输入 char str[100]; cin.get(str,100); -------------------------------------- 使用字符串数据类型的C++字符串 #include < iostream > using namespace std;//命名空间 int main() { string str;//创建字符串对象 cout << "输入:"; getline(cin,str);//读取一行输入 cout << str << endl;//输出字符串对象 return 0; } 好处: 1.动态大小:可以根据需要自动调整大小,无需手动管理内存 2.自动管理内存:自动处理内存分配和释放 3.C++字符串提供了许多方便的成员函数和操作符 4.更安全 5.面向对象的设计
    

C++结构体

struct Person/声明结构体 { char name[50]; int age; float salaly; };//定义结构体类型 Person bill;//定义结构体变量 bill.age = 54;//访问结构体中的成员 --------------------------------- C++结构体指针 struct temp{ ... }; int main(){ temp * P; .... } 在其中访问指针P指向的结构体 使用(*P).name; 先解引用,再访问成员变量 或者可以使用P->name;//一步到位

C++枚举

enum sea{sp,su,st,win}; sea是枚举名,从sp开始,他们的逻辑值为0,1,2,3(默认是如此) enum sea{sp=0,su=4,st=8,win=12} -------------------------------------------------- enum booolean{false,true};//只会创建该枚举变量的蓝图 enum boolean check;//创建了枚举变量 或者可以这样 enum boolean{false,true}check;//这样同样创建了名为check的枚举变量 ------------------------------------------------------- 为什么使用枚举这种奇怪的东西? 1.枚举变量仅仅取许多可能值中的一个值 enum sea{sp,su,st,win}; cout << sizeof(sea);//输出为4,因为整形大小为4(只会存在一个) 2.可读性和可维护性 // 不使用枚举 int status = 1; // 使用枚举 enum Status { OK = 1, ERROR = 2 }; Status status = OK;

C++定义类

1.使用关键字class及其后面跟随的类名定义一个类 2.类的主体在大括号内,并在末尾以分号结束 class name{ //主体 }; ================================================================= --------------示例----------------------- class Test { private://私有关键字,表明只能在同一类中访问 int data1;//定义的类中的变量 float data2; public://公有关键字,可以在类外访问 void func1()//类的方法,无返回值 { data1 = 2; } float func2()//类的方法,有浮点返回值 { data2 = 3.5; return data2; } }; ================================================================= C++的构造函数 class temo { private: int x; float y; public: temo():x(5),y(5.5)//构造函数与其类同名 { //构造函数主体 } ...... }; int main(){ temo t;//一旦创建类的对象后,构造函数就被执行 ....... return 0; } ------------------------------------------------- 当然,你也可以使用这样的构造函数 temo() { x = 5; y = 5.5; }//这种类似于C的语法 你可能就会问了,我直接在类里面将两个变量的值先赋上不就行了 ========================= private: int x = 5; float y = 5.5; 这种写法是 C++11 引入的一种成员变量初始化的方式,叫做成员初始化列表 ========================= private: int x; float y; 这种写法是传统的方式,成员变量 x 和 y 并没有在类的声明中进行初始化。 在构造函数的主体中,通过构造函数的实现进行了显式赋值。 ========================= 实际上,C++11 引入成员初始化列表的主要目的之一是为了提高代码的执行效率。 在一些情况下,使用成员初始化列表可以避免在构造函数体内再次对成员变量进行赋值,从而提高性能。 但在小型的类中,这两种方式的差异可能不太明显。 总体来说,两者的功能是相同的,只是初始化的方式不同。 在现代的 C++ 中,使用成员初始化列表是一种被推荐的做法,因为它可以提高代码的可读性和性能。 ========================= 构造函数也可以重载 不知道什么叫重载? 就是 A(int a,int b){} B(int a,float b){} 这是两个不同的函数,在使用不同参数的情况下可以调用不同的函数 ================================================ 析构函数 与构造函数是一对的,构造是对象被创建 析构函数是对象被销毁 都和类同名,但是析构函数前面加一个“~” ~temo(){ ...... }

C++中的类的继承

class Person//基类 { };//类的定义是需要分号的,它并不是函数,是一个定义的语句 class Teachar : public Person//派生类 { }; class student : public Person//派生类 { }; 派生类与类的声明一起出现后跟冒号,关键字以及基类名 派生类可以访问基类所有成员变量和成员函数 ============================================= C++继承访问权限控制 class base { ... }; class der : public/protected/private base { ... }; -------------------------------------------------------------- | 访问 | public | protected | private | | 同一派生类 | ✔ | ✔ | ✔ | | 派生类 | ✔ | ✔ | ✘ | | 外部类 | ✔ | ✘ | ✘ | -------------------------------------------------------------- 一个派生类继承基类的所有方法,一下例外: 1.基类的构造函数,析构函数,以及拷贝的构造函数 2.基类重载预算符 3.基类友元函数 继承的类型 1.公有继承(public) 基类公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员 基类的私有成员不能直接被派生类使用,但可以通过调用基类的公有和保护成员访问 2.保护继承(protected) 基类公有和保护的成员将成为派生类的保护成员 3.私有继承(private) 基类的公有和保护成员将成为派生类的私有成员 ============================================= C++的多继承 class 派生类名 : 继承类型 基类名1,继承类型 基类名2...... { ......//派生类体 }; ============================================= C++函数重写 继承以后重新在派生类中的函数中使用同样的函数名,重新写一个函数 class base{//基类 public: void getData()//基类方法 { ...... } }; class Der : public base//派生类 { public: void getData()://派生类重写方法 { ...... } } C++很自由,你还可以在派生类的重写方法中,调用基类被重写方法 class base{//基类 public: void getData()//基类方法 { ...... } }; class Der : public base//派生类 { public: void getData()://派生类重写方法 { base :: getData();//重写基类函数中调用基类函数 ...... } }

C++友元函数和友元类

OOD(面向对象的设计)的重要概念之一就是数据隐藏 也就是非成员函数无法访问对象的私有或受保护数据 但是我就是想更自由点,有没有别的方法? 用非成员函数访问私有或受保护的数据 可以通过使用友元函数和友元类 class base { ...... friend int getData(){} ...... } -------------------以下是例子--------------------------- #include < iostream > using namespace std; class B; // 前向声明 B 类 class A { private: int numA; public: A() : numA(12) {} friend int add(A); }; class B { private: int numB; public: B() : numB(12) {} friend int add(B); }; // 在类外定义友元函数 add,可以访问 A 类的私有成员 numA int add(A objA) { return objA.numA; } // 在类外定义友元函数 add,可以访问 B 类的私有成员 numB int add(B objB) { return objB.numB; } int main() { A objA; B objB; // 调用 add 函数并输出结果 cout << "Result from A: " << add(objA) << endl; cout << "Result from B: " << add(objB) << endl; return 0; } 注意到add函数是被重载了的,接受的是不一样的参数 ============================================================== C++中的友元类 当一个类成为另一个类的友元类时 意味着这个类所有成员函数都是另一个类的友元函数 互为友
    

类成员指针

#include< iostream> class Sample { public: void disp() { std::cout << "m=" << m << std::endl; } int m, n; Sample(int a, int b) { this->m = a; this->n = b; } }; int main() { int Sample::* p = & Sample::m;//定义类数据成员指针并赋值 void (Sample:: * ph) () = 0;//类成员函数指针 ph = & Sample::disp;//赋值,不需要加函数的括号 Sample a(10,20); a.*p = 10;//==>a.m = 10 (a.*ph)();//==>a.disp(); }

子对象的构造以及销毁顺序

            
#include< iostream>
class B1
{
public:
	B1()
	{
		std::cout << "B1 创建" << std::endl;
	}
	~B1()
	{
		std::cout << "B1 销毁" << std::endl;
	}
};
class B2
{
public:
	B2()
	{
		std::cout << "B2 创建" << std::endl;
	}
	~B2()
	{
		std::cout << "B2 销毁" << std::endl;
	}
};
class B3
{
public:
	B3()
	{
		std::cout << "B3 创建" << std::endl;
	}
	~B3()
	{
		std::cout << "B3 销毁" << std::endl;
	}
};
class A
{
	B1 b1;//子对象
	B2 b2;//子对象
	B3 b3;//子对象
public:
	A() :b3(), b2(), b1() //打个冒号是初始化列表
	{
		std::cout << "A 创建" << std::endl;
	}
	~A()
	{
		std::cout << "A 销毁" << std::endl;
	}
};
int main()
{
	A a;
}
/*程序过程如下:类比栈来思考
B1 创建
B2 创建
B3 创建
A 创建
A 销毁
B3 销毁
B2 销毁
B1 销毁
*/
            
        

构造与析构的顺序,析构的本质

            
#include< iostream>
class B
{
	int x, y;
public:
	B()
	{
		x = y = 0;
		std::cout << "创建一" << std::endl;
	}
	B(int i)
	{
		x = i; y = 0;
		std::cout << "创建二" << std::endl;
	}
	B(int i, int j)
	{
		x = i; y = j;
		std::cout << "创建三" << std::endl;
	}
	~B()
	{
		std::cout << "销毁" << std::endl;
	}
	void print();
};
void B::print()//类体外定义
{
	std::cout << "x=" << x << ",y=" << y << std::endl;
}
int main()
{
	B* ptr;
	ptr = new B[3];//这里已经创建了对象数组,数组里面有三个对象,没有初始化就是默认构造
	ptr[0] = B();
	ptr[1] = B(5);
	ptr[2] = B(2, 3);
	for (int i = 0; i < 3; i++)
	{
		ptr[i].print();
	}
	delete[] ptr;
}
/*
创建一
创建一
创建一
创建一
销毁
创建二
销毁
创建三
销毁
x=0,y=0
x=5,y=0
x=2,y=3
销毁
销毁
销毁

我们来研究一下为什么会是这个结果
	B* ptr;//对象指针
	ptr = new B[3];
	ptr[0] = B();
	ptr[1] = B(5);
	ptr[2] = B(2, 3);
	创建了一个对象指针
	然后给指针new,也就是堆的方式,动态分配了一个对象数组
	此时数组有三个对象,且没有说明参数
	所以调用的是无参构造,调用三次
	紧接着
	ptr[0] = B();
	赋值运算是从右到左,我们看B()
	创建一个对象并且不带参数,但是对象没有名字,但是也是要调用第一个构造函数
	也就是无名对象,然后ptr[0]=B();
	让ptr[0]指向这个无名参数
	为什么会调用析构呢?
	回顾我们的析构函数定义,其功能是释放对象,并不是删除内存
	所以这里无名对象,其实是一个对象,虽然没有名字,但是确确实实是一个对象
	这个无名对象被人抢了,所以自己就没有存在必要,就会析构
	剩下的没什么了
*/
            
        

构造函数有关细节

            
#include< iostream>
class point
{
	int x1, x2;
public:
	point(int x, int y);
	//这样是不行的,只有说明,没有定义
};
int main()
{
	point data(4, 4);//所以在这里有参构造的时候,系统找不到有参构造函数会报错
	std::cout<<"cg" << std::endl;
}
============================================================================
#include< iostream>
class point
{
	int x1, x2;
public:
	point(int x, int y) { };
	//这样是可以的,只要有函数体了,就算定义了
};
int main()
{
	point data(4, 4);//所以在这里有参构造的时候,系统找到有参构造函数
	std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>
class point
{
	int x1, x2;
public:
	point(int x, int y) { }
	//没错 我把分号去掉以后,竟然还是可以运行
};
int main()
{
	point data(4, 4);
	std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>//函数定义也可以在类体外进行
class point
{
	int x1, x2;
public:
	point(int x, int y);
};
point::point()
{

}
int main()
{
	point data(4, 4);
	std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>//函数定义也可以在类体外进行
class point
{
	int x1, x2;
public:
	point(int x, int y);
};
int main()
{
	point data(4, 4);
	std::cout << "cg" << std::endl;
}
//回过头总结一下
/*
如果只声明没有定义,并且是有参构造
那么在创建对象的时候会报错,因为系统找不到,并且不会退化到默认的无参构造
那可能问题来了,那我创建对象用无参构造呢?
也不行,因为你只要定义了有参构造,系统默认的无参构造就会消失
那我就要无参构造怎么办?自己加一个无参构造函数就行
例如下面的
*/
============================================================================
#include< iostream>
class point
{
	int x1, x2;
public:
	point(int x, int y);
	point() {};
};
int main()
{
	point data();
	std::cout << "cg" << std::endl;
}
            
        

运算符重载

            
//运算符重载有两种
//第一种是重载为类的成员函数
//第二种是重载为类的友元函数
#include< iostream>
class Point
{
private:
	int x, y;
public:
	Point() {};
	Point(int a, int b) { x = a; y = b; };
	void print() { std::cout << x << "," << y << std::endl; };
	Point operator+(Point& a)//本质就是个成员函数,成员函数可以访问私有变量
	{
		return Point(x + a.x, y + a.y);
	};
	//没错,重载这个+号,其实就是将两个Point对象的+法得以实现
	//它的左值+右值,右值就是参数,左值呢?是this指针,这里x本质是this.x
};
int main()
{
	Point p1(1, 1), p2(2, 2), p3;
	p3 = p1 + p2;
	p3.print();

}//输出3,3
==========================================================================
#include< iostream>
class Point
{
private:
	int x, y;
public:
	Point() {};
	Point(int a, int b) { x = a; y = b; };
	void print() { std::cout << x << "," << y << std::endl; };
	friend Point operator+(Point& a, Point& b)//本质就是个友元函数,可以访问私有变量
	{
		return Point(a.x + b.x, a.y + b.y);
	};
	//没错,重载这个+号,其实就是将两个Point对象的+法得以实现
	//值得注意的是,友元没有this指针,所以,它访问左值的时候
	//是通过参数,也就是说友元是传两个参数
};
int main()
{
	Point p1(1, 1), p2(2, 2), p3;
	p3 = p1 + p2;//既然是重载,这里的+号,编译器会自动判断+左右对象来执行我们重载的+
	p3.print();

}//输出3,3
//所以思考一下,p1+p2和p2+p1一样吗?是一样的,但是不可交换对于重载为成员函数来讲
//但是重载为友元函数就可以交换,因为是两个形参,不需要this指针
==========================================================================
//一元自加和自减重载
#include< iostream>
class Point
{
private:
	int x, y;
public:
	Point() {};
	Point(int a, int b) { x = a; y = b; };
	void print() { std::cout << x << "," << y << std::endl; };
	void operator++()//这是重载前置++,别管我函数体内怎么写,那是我重载的操作
	{
		x++; y++;
	};
	void operator++(int)//括号里面有int,说明是重载后置++
	{
		x++; y++;
	}

};
int main()
{
	Point p1(1, 1), p2(2, 2), p3;
	p1++;
	++p2;

}
            
        

模板

            
#include< iostream>
template< class T>//函数模板
T min(T x, T y)
{
	if (x < y)
		return x;
	else
		return y;
}//这里的T实际上是占位符,系统根据传入的参数判断类型,然后用类型替换T
int main()
{
	int n1 = 2, n2 = 10;
	double d1 = 1.5, d2 = 5.6;
	std::cout << "交小者为" << min(n1, n2) << std::endl;//min调用的时候是生成了模板函数,注意区分
	std::cout << "较小者为" << min(d1, d2) << std::endl;//这个过程就是函数模板的实例化
}//模板函数的说明和定义必须是全局作用域,并且不能被说明为类的成员函数
//其次模板函数不具备类型隐式转换-》int变double,char变int
==========================================================================
#include< iostream>
using namespace std;
template < class T>//类模板
class Sample
{
    T d;
public:
    Sample(T i)
    {
        d = i;
    };
    void disp()
    {
        cout << "d=" << d << endl;
    };
};

int main() // 更改返回类型为int
{
    Sample < int> obj1(10);
    Sample< char> obj2('a'); // 确保Sample模板类能够处理char类型的参数
    Sample< const char*> obj3("China");
    //这里我一开始是按照书本上敲的是 Sample< char *>obj3("China");
    //一直报错,原来是"China"是字符字面量,就是不可修改的,也就是对应的应该是
    //const char *类型
    //字符字面量就是字面上的量,就是写的是什么就是什么
    obj1.disp();
    obj2.disp();
    obj3.disp();
    return 0; // 添加返回值
}
==========================================================================
            
        

继承与派生

        
#include< iostream>
using namespace std;
class x
{
protected:
	int a;
public:
	x()//构造函数
	{
		a = 10;
	}
};
class x1 :public x//公有继承
{
public:
	x1()
	{
		cout << a << endl;
	}
};
class x2 :public x //公有继承
{public:
	x2()
	{
		cout << a << endl;
	}
};
class y :x1, x2
{
public:
	y()
	{
		cout << a << endl;//这里报错,程序指明了y::a不明确
	}//也就是二义性,它不知道是x1的a还是x2的a
	//x1,x2的a都是通过继承x得来的,也就是说x1和x2都会得到x1关于a的一个新物体
};
void main()
{
	y obj;
}
//怎么办???
======================================================================
//虚继承要求同一个子类的多个父类继承自同一个间接父类
//其它的情况,即便是虚继承也就是普通继承
#include< iostream>
using namespace std;
class x
{
protected:
	int a;
public:
	x()//构造函数
	{
		a = 10;
	}
};
class x1 :virtual public x//虚公有继承
{
public:
	x1()
	{
		cout << a << endl;
	}
};
class x2 :virtual public x //虚公有继承
{
public:
	x2()
	{
		cout << a << endl;
	}
};
class y :x1, x2
{
public:
	y()
	{
		cout << a << endl; //这里程序指明的是x的a
		//其实原理是这样的,x1 ,x2中继承的x的a是指向一个的
		//是公有的一个,实现方式是虚指针,指向虚表,虚指针是存放偏移量
		//偏移量是a到类的偏移
		//也就是说a成为了单独的一个了
		//你在x1中修改a,那么x2中的a也会改变
	}
};
void main()
{
	y obj;
}
========================================================================
/*
派生类的构造函数执行顺序:
            先执行所有基类的构造函数,再执行派生类本身的构造函数
            同一层次的各基类构造函数执行顺序取决于定义派生类时所指定的各基类顺序
            与派生类构造函数中所定义的成员初始化列表的各项顺序无关
            包含虚基类的构造3原则:
            1.虚基类的构造函数在非虚基类之前调用
            2.同一层次中包含多个虚基类,按照说明的顺序调用
            3.若虚基类由非虚基类派生,则仍然先调用基类构造函数
            */
#include< iostream>
using namespace std;
class base1
{
public:
	base1()
	{
		cout << "base1" << endl;
	}
};
class base2
{
public:
	base2()
	{
		cout << "base2" << endl;
	}
};
class level1 :public base2, virtual public base1//我知道可能这里会出现一点疑点
{//为什么base1没有再被构造?以为前面已经构造了,虚继承的这里就不需要再构造,共用前面的
public:
	level1()
	{
		cout << "level1" << endl;
	}
};
class level2 :public level1, virtual public base1
{
public:
	level2()
	{
		cout << "level2" << endl;
	}
};
class top :public level1, virtual public level2
{
public:
	top()
	{
		cout << "top" << endl;
	}
};
void main()
{
	top obj;
}
/*打印结果
*
base1
base2
level1
level2
base2
level1
top
*/
//注意嗷,这里单纯是构造函数的顺序,析构函数相当于把这出栈
========================================================================
类模板从类模板派生
template < class T>
class A
    {

    };
template < class T1,class T2>
class B :public A< T2>//必须指明原本有的参数
    {

    };
初始化的时候也是
B< int ,int > obj;这样,后面那个int送给A当它的狗粮
========================================================================
类模板从非模板类派生
#include< iostream>
using namespace std;
class A
{
	int a;
public :
	A(int i)//构造函数,并且一旦你动了有参构造,系统提供的免费无参构造函数就不免费了,不给你了
	{
		a = i;
	}
	//就和我们的童年一样,一旦你想长大,并且真的长大了,没有人再呵护你的小心思了
	//除非你自己呵护自己
	//怎么说?
	/*看下面
	A()
	{
	  scou<<"这是我自己给自己创建的无参构造"<< endl;
	}
	*/
};
template < class T1>
class B :public A//继承非类模板A,注意区分模板类和类模板,中国人喜欢重要的放后面,你应该懂我意思
{
	T1 b;
public:
	B(T1 x, int y):A(y)//这里再一次说明了,一定为没有默认构造函数的基类进行有参构造的初始化
	{
		b = x;
	}
};//不懂也没关系,模板类是模板生成的类,类模板是一个模板
========================================================================
非类模板从类模板派生
#include< iostream>
using namespace std;
template< class T1>
class A
{

};
class B :public A< int>//必须指明实际的类型,就是int啊,char啊,这些,不允许虚空打靶
{

};//没关系,虚空打靶是我即兴想的词语。
        
    

多态性和虚函数

            
#include< iostream>
using namespace std;
class Point
{
	int x, y;
public:
	Point(int x1,int y1)
	{
		x = x1;
		y = y1;
	}
	int area()
	{
		return 0;
	}
};
class Rect :public Point
{
	int l, w;
public:
	Rect(int x1, int y1, int l1, int w1) :Point(x1, y1)
	{
		l = l1;
		w = w1;
	}
	int area()
	{
		return l * w;
	}
};
void fun(Point& p)//静态联编这里是默认到Point的,程序只有在运行的时候才知道是束缚到Rect上
{
	cout << p.area() << endl;
}
void main()
{
	Rect rec(2, 4, 10, 6);
	fun(rec);
}//程序输出为0
//因为是静态联编
//C++规定动态联编是在虚函数支持下实现的
//静态联编和动态联编属于多态性
//虚函数是动态联编的基础,虚函数是成员函数,且为非static
//一个类中的成员函数被说明为虚函数,就意味着派生类中可能有不一样的定义
//让其在运行时进行关联或束定
//当派生类虚函数与基类虚函数的参数不同时,派生类的虚函数讲将丢失虚特性,变成重载函数
class a {
	virtual int m(int x) = 0;// 纯虚函数,就是基类没有对虚函数进行定义,是一种特殊的虚函数
	//子类定义以后,自动选择动态联编
};
//像这种带有纯虚函数的类a是抽象类
//不能建立抽象类对象
//抽象类不能用作参数类型、函数返回值、显示转换
//可以说明指向抽象类的指针和引用,此指针可以指向它的派生类,实现多态
===============================================================================
#include< iostream>
using namespace std;
class Point
{
	int x, y;
public:
	Point(int x1,int y1)
	{
		x = x1;
		y = y1;
	}
	virtual int area()
	{
		return 0;
	}
};
class Rect :public Point
{
	int l, w;
public:
	Rect(int x1, int y1, int l1, int w1) :Point(x1, y1)
	{
		l = l1;
		w = w1;
	}
	virtual int area()//这里即便不说明virtual也是虚函数
	{
		return l * w;
	}
};
void fun(Point& p)
{
	cout << p.area() << endl;
}
void main()
{
	Rect rec(2, 4, 10, 6);
	fun(rec);
}
//输出60
//你也看到了,虚函数其实实现的是程序在何时选择执行哪个
//这也是多态的表现,用点中二的语言,不知道你看英雄联盟双城之战了没有
//杰斯穿越时空,多个时间线击杀维克托,虽然结果可能一样,但是过程绝不是一样
//也就是有多个态的表现形式,同一个物质在不同作用下的不同表现
//还有虚析构函数
======================================================================
#include< iostream>
using namespace std;
class A
{
public:
	virtual void fun1(){cout << "A fun1" << endl;}
	virtual void fun2() { cout << "A fun2" << endl;}
	void fun3() { cout << "A fun3" << endl; }
	void fun4() { cout << "A fun4" << endl; }
};
class B :public A
{
public:
	virtual void fun1() { cout << "B fun1" << endl; }
	virtual void fun2(int x) { cout << "B fun2" << endl; }
	virtual void fun3() { cout << "B fun3" << endl; }
	void fun4() { cout << "B fun4" << endl; }
};
void main()
{
	A* p;
	B b;
	p = & b;
	p->fun1();//虚函数动态联编
	p->fun2();//由于基类和派生类虚函数参数类型不一致,执行静态联编
	p->fun3();//基类并没有说明虚函数,子类说明了,但是子类只能和它的子类,也就是子类的子类形成动态
	p->fun4();
}
/*输出
B fun1
A fun2
A fun3
A fun4
*/
            
        

异常处理

            
#include< iostream>
using namespace std;
class Sample
{
public:
	Sample()//构造函数
	{
		cout << "创建" << endl;
		throw 1;//throw就像一个打棒球的投手,投出一个球
	}
	~Sample()//析构函数
	{
		cout << "销毁" << endl;
	}
};
int main()
{
	try//try就是这个球场,棒球只有在棒球场中才能发挥作用
	{
		Sample s;
	}
	catch (int)//这是拿棒子准备打球的,但是它不会看具体的球的型号,只会知道这个球是棒球
	{
		cout << "出现异常" << endl;//这里是棒球接到以后执行的动作
	}
}
//输出
/*
创建
出现异常
*/
//为什么析构没有被调用?因为对象还没有创建完成,只是在构造函数这一步抛出了异常停止了对象创建