C++基础学习三 C++中的引用 引用基本语法 语法 数据类型 &别名 = 原名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std;int main () { int a=10 ; int &b=a; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; b=100 ; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; }
引用必须要初始化
int &b;//(X)
引用一旦初始化,就不可以更改了
引用做函数参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 void swap_01 (int a, int b) { int t; t = a; a = b; b = t; } void swap_02 (int *a, int *b) { int t; t = *a; *a = *b; *b = t; } void swap_03 (int &a, int &b) { int t; t = a; a = b; b = t; } int main () { int a=10 ; int b=20 ; cout<<"-----原ab-----" <<endl; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; swap_01 (a, b); cout<<"-----swap_01-----" <<endl; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; swap_02 (&a, &b); cout<<"-----swap_02-----" <<endl; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; swap_03 (a, b); cout<<"-----swap_03-----" <<endl; cout<<"a=" <<a<<endl; cout<<"b=" <<b<<endl; }
引用做函数返回值 不要返回局部变量的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int & test01 () { int a=10 ; return a; } int & test02 () { static int a=10 ; return a; } int main () { int &p2 = test02 (); cout<<"p2 = " <<p2<<endl; cout<<"p2 = " <<p2<<endl; test02 () = 100 ; cout<<"p2 = " <<p2<<endl; cout<<"p2 = " <<p2<<endl; return 0 ; }
引用的本质 本质:相当于C++的指针常量
常量引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void show (const int &a) { cout<<"a=" <<a<<endl; } int main () { int a = 10 ; int &p1 = a; const int &p2 = 10 ; show (a); }
函数高级 函数的默认参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std;int fun (int a, int b, int c=20 ) { return a+b+c; } int main () { cout<<fun (10 , 30 )<<endl; cout<<fun (10 ,20 ,40 )<<endl; return 0 ; }
函数的占位参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void func02 (int a, int =10 ) { cout<<"this is func02" <<endl; } int main () { int a=10 ; func02 (10 ,); return 0 ; }
函数重载 作用:函数名可以相同,提高重复利用
函数重载满足条件:
同一个作用域下
函数名称相同
函数参数类型不同,或者个数不同,或者顺序不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void func () { cout<<"调用func03()" <<endl; } void func (int a) { cout<<"调用func03(int a) a=" <<a<<endl; } void func (double b) { cout<<"调用func03(double b) b=" <<b<<endl; } void func (int a, double b) { cout<<"调用func03(int a, double b) a=" <<a<<" b=" <<b<<endl; } int main () { int a=5 ; double b=3.14 ; func (); func (a); func (b); func (a, b); }
函数重载注意事项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void func (int &a) { cout<<"func()" <<endl; } void func (const int &a) { cout<<"const func()" <<endl; } void func_a (int a, int b=10 ) { cout<<"func(int a, int b=10)" <<endl; } void func_a (int a) { cout<<"func(int a)" <<endl; } int main () { int a=10 ; return 0 ; }
类和对象 封装 封装的意义 封装是C++面向对象的三大特征之一
封装的意义:
将属性和行为作为一个整体,表现生活中的事项
将属性和行为加以权限控制
案例 例1:设计一个圆类,求圆的周长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include "iostream" using namespace std;const double PI=3.14 ;class Circle { public : int r; double calcuate_C () { return 2 *PI*r; } }; int main () { Circle c; c.r = 10 ; cout<<"圆的周长为" <<c.calcuate_C ()<<endl; return 0 ; }
例2:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Student { public : string name; int id; void ShowMsg () { cout<<"姓名:" <<name<<" 学号:" <<id<<endl; } void Setname (string m_name) { name = m_name; } }; int main () { Student stu1, stu2; stu1.name = "Lihua" ; stu1.id = 24 ; stu1.ShowMsg (); stu2.name = "Daming" ; stu2.id = 20 ; stu2.ShowMsg (); stu2.Setname ("Aliy" ); stu2.ShowMsg (); return 0 ; }
访问权限
公共权限 public 成员 类内可以访问 类外可以访问
保护权限 protected 成员 类内可以访问 类外不可以访问 儿子也可以访问父亲中的保护内容
私有权限 private 成员 类内可以访问 类外不可以访问 儿子不可以访问父亲中的保护内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Person { public : string name; protected : string car; private : int Password; void func () { name = "Dad" ; car = "BWM" ; Password = 16801 ; } }; int main () { Person p; p.name = "Father" ; cout<<"name:" <<p.name<<endl; return 0 ; }
struct与class的区别 在C++中的struct和class唯一的区别就在于默认的访问权限不同
区别:
struct默认权限为公共权限
class默认权限为私有权限
成员属性私有化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class Person { public : void Setname (string m_name) { name = m_name; } string Getname () { return name; } void SetAge (int m_age) { if (age<0 || age>150 ) { m_age = 0 ; return ; } age=m_age; } int Getage () { return age; } void Setlover (string lover_name) { lover = lover_name; } string Getlover () { return lover; } private : string name; int age; string lover; }; int main () { Person p; p.Setname ("Daming" ); p.SetAge (20 ); p.Setlover ("Lily" ); cout<<"NAME:" <<p.Getname ()<<endl; cout<<"AGE:" <<p.Getage ()<<endl; cout<<"LOVER:" <<p.Getlover ()<<endl; return 0 ; }
设计案例1:立方体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Cube { public : void SetLWH (float c_l, float c_w, float c_h) { l = c_l; w = c_w; h = c_h; } float Getarea () { return 2 *(l*w+l*h+w*h); } float Getvolume () { return l*w*h; } private : float l; float w; float h; }; int main () { Cube c; c.SetLWH (9 , 5.3 , 4 ); cout<<"立方体面积:" <<c.Getarea ()<<endl; cout<<"立方体体积:" <<c.Getvolume ()<<endl; return 0 ; }
设计案例2:点和圆的关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Circle_point { public : float point_x, point_y; float circle_x, circle_y, circle_r; void Getpose () { float d = (point_x-circle_x)*(point_x-circle_x)+(point_y-circle_y)*(point_y-circle_y); if (d>circle_r*circle_r) { cout<<"点在圆外" <<endl; } else if (d<circle_r*circle_r) { cout<<"点在圆内" <<endl; } else { cout<<"点在圆上" <<endl; } } }; int main () { Circle_point c; c.point_x = 3 ; c.point_y = 3 ; c.circle_x = 0 ; c.circle_y = 0 ; c.circle_r = 3 ; c.Getpose (); return 0 ; }
对象特性 对象的初始化和清理
构造函数:主要作用在于创建对象时对象的成员属性赋值,构造函数有编译器自动调用,无需手动调用
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作
构造函数语法:类名(){}
构造函数,没有返回值不写void
函数名称与类名相同
构造函数可以有参数,因此可以发生重载
程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次
析构函数语法:~类名(){}
析构函数,没有返回值不写void
函数名称与类名相同,在名称前加上符号~
析构函数不可以有参数,因此不可以发生重载
程序在对象销毁前自动调用析构函数,无需手动调用,而且只会调用一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "iostream" #include "string" using namespace std;class Person { public : Person () { cout<<"Person 构造函数调用" <<endl; } ~Person () { cout<<"Person 析构函数调用" <<endl; } }; int main () { Person p; return 0 ; }
构造函数的分类及调用 两种分类方式:
按参数分为:有参构造和无参构造
按类型分为:普通构造和拷贝构造
三种调用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include "iostream" #include "string" using namespace std;class Person { public : Person () { cout<<"Person 构造函数调用" <<endl; } Person (int a) { age = a; cout<<"Person(int a) 构造函数调用 a=" <<a<<endl; } Person (const Person &P) { age = P.age; cout<<"Person(const Person &P) 构造函数调用 age=" <<age<<endl; } ~Person () { cout<<"Person 析构函数调用" <<endl; } int age; }; void test () { cout<<"----------------" <<endl; Person p1; cout<<"----------------" <<endl; Person p2 (10 ) ; cout<<"----------------" <<endl; Person p3 (p2) ; cout<<"----------------" <<endl; Person p4 = Person (p3); cout<<"----------------" <<endl; Person p5 = 10 ; } int main () { test (); }
拷贝构造函数和调用时机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include "iostream" #include "string" using namespace std;class Person { public : Person () { cout<<"Person 构造函数调用" <<endl; } Person (int a) { age = a; cout<<"Person(int a) 构造函数调用 a=" <<a<<endl; } Person (const Person &P) { age = P.age; cout<<"Person(const Person &P) 构造函数调用 age=" <<age<<endl; } ~Person () { cout<<"Person 析构函数调用" <<endl; } int age; }; void doWork (Person p) ;void test01 () { Person p1 (20 ) ; Person p2 (p1) ; } void test02 () { Person p; doWork (p); } void doWork (Person p) { cout<<"doWork...." <<endl; } Person doWork01 () { Person p; return p; } void test03 () { Person p = doWork01 (); } int main () { test01 (); cout<<"****************" <<endl; test02 (); cout<<"****************" <<endl; test03 (); return 0 ; }
构造函数调用规则
默认情况下,c++编译至少给一个类添加3个函数
默认构造函数(无参, 函数体为空)
默认析构函数(无参, 函数体为空)
默认拷贝构造函数,对属性进行值拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Person { public : int age; Person () { cout<<"Person的构造函数" <<endl; } Person (const Person &p) { cout<<"Person的拷贝构造函数" <<endl; age = p.age; } ~Person () { cout<<"Person的析构函数" <<endl; } }; int main () { Person p; p.age = 18 ; Person p1 (p) ; cout<<"p1.age=" <<p1.age<<endl; return 0 ; }
初始化列表 作用:C++提供了初始化的列表语法,用来初始化属性
语法:构造函数():属性1(值1), 属性2(值2).....{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Person { public : Person (int a, int b, int c):A (a), B (b), C (c) { } int A, B, C; }; int main () { Person p (1 , 2 , 3 ) ; cout<<"A=" <<p.A<<endl; cout<<"B=" <<p.B<<endl; cout<<"C=" <<p.C<<endl; return 0 ; }
类对象作为类成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Phone { public : Phone (string name):pname (name) { } string pname; }; class Person { public : Person (string n, string pn):name (n), p (pn) { } string name; Phone p; }; int main () { Person p ("Daming" , "PhoneMAX" ) ; cout<<p.name<<" take a phone named " <<p.p.pname<<endl; return 0 ; }
静态成员 静态成员变量:
所有对象都共享同一份数据
编译阶段就分配内存
类内声明,类外初始化操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class A { public : static int a; }; int A::a = 100 ;int main () { A one; cout<<"one.a=" <<one.a<<endl; A two; two.a = 200 ; cout<<"one.a=" <<one.a<<endl; cout<<"A::a=" <<A::a<<endl; return 0 ; }
静态成员函数
所有对象共享同一函数
静态成员函数只能访问静态成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Person { public : static void func () { a = 90 ; cout<<"static void func()" <<endl; } static int a; int b; }; int main () { Person p; p.func (); cout<<"------------------" <<endl; Person::func (); return 0 ; }
成员变量和成员函数分开存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Person { public : int a; static int b; void func () {} static void test () {} }; int Person::b = 10 ;void test01 () { Person p; cout<<"sizeof Person=" <<sizeof (p)<<endl; } void test02 () { Person p; cout<<"sizeof Person=" <<sizeof (p)<<endl; } int main () { test02 (); return 0 ; }
this指针 C++通过特殊的对象指针,this指针,解决上述问题, this指针指向被调用的成员函数所属的对象
this指针是隐含 每一个非静态成员函数内的指针
this指针不需要定义 ,直接使用即可
this指针的用途
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,使用return *this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Person { public : Person (int age) { this ->age = age; } Person& Addage (Person &p) { this ->age += p.age; return *this ; } int age; }; int main () { Person p1 (18 ) ; cout<<"Person p1 age=" <<p1.age<<endl; cout<<"-------------" <<endl; Person p2 (10 ) ; p2.Addage (p1); cout<<"Person p2 age=" <<p2.age<<endl; cout<<"-------------" <<endl; p2.Addage (p1); cout<<"Person p2 age=" <<p2.age<<endl; return 0 ; }
空指针访问成员函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { public : void showMsg () { cout<<"void showMsg()" <<endl; } void showAge () { cout<<"void showAge() age=" <<this ->age<<endl; } int age; }; int main () { Person *p = NULL ; p->showMsg (); return 0 ; }
const 修饰成员函数 常函数
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性同时加关键字mutable后,在常函数中依然可以修改
常对象
声明对象加const称该对象为常对象
常对象只调用常函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person { public : void showPerson () const { this ->b = 100 ; } void func () { } int a; mutable int b; }; int main () { const Person p{}; cout<<"p.b=" <<p.b<<endl; return 0 ; }
友元 全局函数做友元 在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include "iostream" #include "string" using namespace std;class Building { friend void GoodGay (Building *building) ; public : Building () { SittingRoom = "客厅" ; BedRoom = "卧室" ; } public : string SittingRoom; private : string BedRoom; }; void GoodGay (Building *building) { cout<<"好基友全局函数 正在访问:" <<building->SittingRoom<<endl; cout<<"好基友全局函数 正在访问:" <<building->BedRoom<<endl; } void test01 () { Building building; GoodGay (&building); } int main () { test01 (); return 0 ; }
类做友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Building { public : string SettingRoom; Building (); friend class GoodGay ; private : string BedRoom; }; Building::Building () { SettingRoom = "客厅" ; BedRoom = "卧室" ; } class GoodGay { public : void visit () ; GoodGay (); Building * building; }; GoodGay::GoodGay () { building = new Building; } void GoodGay::visit () { cout<<"好基友类正在访问:" <<building->SettingRoom<<endl; cout<<"好基友类正在访问:" <<building->BedRoom<<endl; } int main () { GoodGay g; g.visit (); return 0 ; }
成员函数做友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Building ;class GoodGay { public : GoodGay (); void visit () ; Building * building; }; GoodGay::GoodGay () { building = new Building; }; void GoodGay::visit () { cout<<"好基友类正在访问:" <<building->SettingRoom<<endl; cout<<"好基友类正在访问:" <<building->BedRoom<<endl; } class Building { public : friend void GoodGay::visit () ; public : string SettingRoom; Building (); private : string BedRoom; }; Building::Building () { SettingRoom = "客厅" ; BedRoom = "卧室" ; } int main () { GoodGay g; g.visit (); return 0 ; }
C++运算符重载(operator) 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
加号运算符重载 通过自己写成员函数,实现两个对象相加属性后返回新的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Person { public : int a; int b; }; Person operator +(Person &p1, Person &p2) { Person t; t.a = p1.a + p2.a; t.b = p1.b + p2.b; return t; } Person operator +(Person &p, int num) { Person t; t.a = p.a + num; t.b = p.b + num; return t; } int main () { Person p1, p2; p1.a = 10 ; p1.b = 5 ; p2.a = 8 ; p2.b = 12 ; Person p3 = operator +(p1, p2); cout<<"p3.a=" <<p3.a<<endl; cout<<"p3.b=" <<p3.b<<endl; Person p4 = p1 + 10 ; cout<<"p4.a=" <<p4.a<<endl; cout<<"p4.b=" <<p4.b<<endl; return 0 ; }
左移运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { public : int a; int b; }; ostream &operator <<(ostream &cout, Person &p) { cout<<"a=" <<p.a<<" b=" <<p.b; return cout; } int main () { Person p; p.a = 15 ; p.b = 10 ; cout<<p<<endl; return 0 ; }
递增运算符重载 作用:通过重载递增运算符重载,实现自己的整形数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Myint { public : friend ostream & operator <<(ostream &cout, Myint &p); Myint () { num = 0 ; } Myint& operator ++() { ++num; return *this ; } int operator ++(int ) { Myint t = *this ; num++; return num; } private : int num; }; ostream & operator <<(ostream &cout, Myint &p) { cout<<p.num; return cout; } int main () { Myint p; cout<<p<<endl; cout<<p++<<endl; cout<<++p<<endl; return 0 ; }
赋值运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Person { public : Person (int age) { Age = new int (age); } ~Person () { if (Age != NULL ) { delete Age; Age = NULL ; } } Person& operator =(Person &p) { if (Age != NULL ) { delete Age; Age = NULL ; } Age = new int (*p.Age); return *this ; } int *Age; }; int main () { Person p1 (18 ) ; Person p2 (20 ) ; cout<<"-------------" <<endl; cout<<"p1的年龄为:" <<*p1.Age<<endl; cout<<"-------------" <<endl; cout<<"p2的年龄为:" <<*p2.Age<<endl; p2 = p1; cout<<"-------------" <<endl; cout<<"p2的年龄为:" <<*p2.Age<<endl; return 0 ; }
关系运算符重载 相等运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Person { public : Person (string n, int a) { name = n; age = a; } bool operator ==(Person &p) { if ((this ->name == p.name) && (this ->age==p.age)) { return true ; } return false ; } string name; int age; }; int main () { Person p1 ("Daming" , 18 ) ; Person p2 ("Lily" , 19 ) ; if (p1==p2) { cout<<"p1和p2相等" <<endl; } else { cout<<"p1和p2不相等" <<endl; } return 0 ; }
不等运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Person { public : Person (string n, int a) { name = n; age = a; } bool operator ==(Person &p) { if ((this ->name == p.name) && (this ->age==p.age)) { return true ; } return false ; } bool operator !=(Person &p) { if ((this ->name != p.name) || (this ->age!=p.age)) { return true ; } return false ; } string name; int age; }; int main () { Person p1 ("Daming" , 18 ) ; Person p2 ("Lily" , 19 ) ; if (p1!=p2) { cout<<"p1和p2不相等" <<endl; } else { cout<<"p1和p2相等" <<endl; } return 0 ; }
同理可以大小比较运算符重载
函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Print { public : void operator () (string test) { cout<<"str= " <<test<<endl; } }; class ADD { public : int operator () (int a, int b) { return (a+b); } }; int main () { Print print; print ("hello C++" ); ADD num; int t=num (3 , 2 ); cout<<"t=" <<t<<endl; cout<<ADD ()(20 , 30 )<<endl; return 0 ; }
继承 基本语法 class 子类 : 继承方式 父类
未继承前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class JAVA { public : void header () { cout<<"首页、公开课、登录、注册" <<endl; } void footer () { cout<<"帮助中心、交流合作、站内地图。。。(公共底部)" <<endl; } void left () { cout<<"Java、python、c/c++。。。。" <<endl; } void content () { cout<<"Java学科" <<endl; } }; class Python { public : void header () { cout<<"首页、公开课、登录、注册" <<endl; } void footer () { cout<<"帮助中心、交流合作、站内地图。。。(公共底部)" <<endl; } void left () { cout<<"Java、python、c/c++。。。。" <<endl; } void content () { cout<<"Python学科" <<endl; } }; void test01 () { JAVA j; cout<<"-------JAVA------" <<endl; j.header (); j.footer (); j.left (); j.content (); cout<<"-------------------" <<endl; Python p; cout<<"-------Python------" <<endl; p.header (); p.footer (); p.left (); p.content (); cout<<"--------------------" <<endl; } int main () { test01 (); return 0 ; }
继承后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Basic { public : void header () { cout<<"首页、公开课、登录、注册" <<endl; } void footer () { cout<<"帮助中心、交流合作、站内地图。。。(公共底部)" <<endl; } void left () { cout<<"Java、python、c/c++。。。。" <<endl; } }; class JAVA :public Basic{ public : void content () { cout<<"Java学科" <<endl; } }; class Python :public Basic{ public : void content () { cout<<"Python学科" <<endl; } };
优点:
减少重复代码
继承方式
公共继承public
保护继承priotected
私有继承private
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class One { public : int a; protected : int b; private : int c; }; class Two :public One{ public : void func () { a = 10 ; b = 20 ; } }; class Three : protected One{ public : void func () { a = 10 ; b = 20 ; } }; class Four : private One{ public : void func () { a = 10 ; b = 20 ; } };
继承中的对象模型
父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性 是被编译器给隐藏,因此访问不到,但是会被继承下去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class A { public : int a; protected : int b; private : int c; }; class B :public A{ public : int d; }; int main () { B demo; cout<<"sizeof demo=" <<sizeof (demo)<<endl; return 0 ; }
构造和析构顺序 先有父类后有子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Basic { public : Basic () { cout<<"Basic的构造函数" <<endl; } ~ Basic () { cout<<"Basic的析构函数" <<endl; } }; class Son :public Basic{ public : Son () { cout<<"Son的构造函数" <<endl; } ~ Son () { cout<<"Son的析构函数" <<endl; } }; int main () { Son s; return 0 ; }
同名成员处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Basic { public : int a=10 ; }; class Son :public Basic{ public : int a=5 ; }; int main () { Son s; cout<<"s.a=" <<s.a<<endl; cout<<"s.Basic::a=" <<s.Basic::a<<endl; s.func (); s.Basic::func (); return 0 ; }
同名静态成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Basic { public : static int a; }; int Basic::a=10 ;class Son :public Basic{ public : static int a; }; int Son::a=100 ;int main () { Son s; cout<<"s.a=" <<s.a<<endl; cout<<"s.Basic::a=" <<s.Basic::a<<endl; cout<<"Son::a=" <<Son::a<<endl; cout<<"Son::Basic::a=" <<Son::Basic::a<<endl; return 0 ; }
多继承语法 允许一个类继承多个类
语法: class 子类 :继承方式 父类1, 继承方式 父类2
菱形继承 案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Animals { public : int age = 10 ; }; class Sheep : virtual public Animals{ public : int age=8 ; }; class Camel : virtual public Animals{ public : int age=5 ; }; class Alpaca :public Sheep, public Camel{ public : int age; }; int main () { Alpaca a; cout<<"a.Sheep::age=" <<a.Sheep::age<<endl; cout<<"a.Camel::age=" <<a.Camel::age<<endl; return 0 ; }
多态 基本概念及语法 多态是C++面向对象三大特性之一
多态分为两类:
静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行多态
静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
条件:
动态多态使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Animal { public : virtual void speak () { cout<<"动物在说话" <<endl; } }; class Cat :public Animal{ public : void speak () { cout<<"小猫喵喵喵" <<endl; } }; class Dog :public Animal{ public : void speak () { cout<<"小狗汪汪汪" <<endl; } }; void doSpeak (Animal &animal) { animal.speak (); } int main () { Cat cat; doSpeak (cat); Dog dog; doSpeak (dog); return 0 ; }
多态原理剖析 多态案例—-计算器类
案例描述
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
代码组织结构清晰
可读性强
利于前期和后期的扩展以及维护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include "iostream" #include "string" using namespace std;class Calculator { public : int getRes (string oper) { if (oper =="+" ) { return num1+num2; } else if (oper =="-" ) { return num1-num2; } else if (oper =="*" ) { return num1*num2; } } int num1; int num2; }; class AbstracCalc { public : virtual int getRes () { return 0 ; } int num1; int num2; }; class AddCalc :public AbstracCalc{ public : int getRes () { return num1 + num2; } }; class MulCalc :public AbstracCalc{ public : int getRes () { return num1 * num2; } }; class SubCalc :public AbstracCalc{ public : int getRes () { return num1 - num2; } }; void test01 () { Calculator c; c.num1 = 10 ; c.num2 = 20 ; cout<<"---test01---" <<endl; cout<<c.num1<<"+" <<c.num2<<"=" <<c.getRes ("+" )<<endl; } void test02 () { cout<<"---test02---" <<endl; AbstracCalc * p = new AddCalc; p->num1 = 10 ; p->num2 = 20 ; cout<<p->num1<<"+" <<p->num2<<"=" <<p->getRes ()<<endl; delete p; p = new SubCalc; p->num1 = 10 ; p->num2 = 20 ; cout<<p->num1<<"-" <<p->num2<<"=" <<p->getRes ()<<endl; delete p; p = new MulCalc; p->num1 = 10 ; p->num2 = 20 ; cout<<p->num1<<"*" <<p->num2<<"=" <<p->getRes ()<<endl; delete p; } int main () { test01 (); test02 (); return 0 ; }
多态实现好处:
如果想扩展新功能,需求修改源码
在真是开发中,提倡开闭原则
开闭原则:对扩展进行开放,对修改进行关闭
纯虚函数与抽象类 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
大此可以将虚函数改为纯虚函数
纯虚函数语法: virtual 返回值类型 函数名〔参数列表)= 0
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
无法实例化对象
子类必须重写 抽象类中的纯虚函数,否则也属于抽象类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Base {public : virtual void func () = 0 ; }; class Son :public Base{ public : virtual void func () { cout<<"func调用" <<endl; } }; int main () { Base *base = new Son; base->func (); return 0 ; }
多态案例二-制作饮品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 class AbstractDrink { public : virtual void Boil () =0 ; virtual void Brew () =0 ; virtual void PourInCup () =0 ; virtual void PutSomething () =0 ; void Make () { Boil (); Brew (); PourInCup (); PutSomething (); } }; class Coffee :public AbstractDrink{ public : virtual void Boil () { cout<<"煮水" <<endl; } virtual void Brew () { cout<<"冲泡咖啡" <<endl; } virtual void PourInCup () { cout<<"放入杯子" <<endl; } virtual void PutSomething () { cout<<"加糖" <<endl; } }; class Milktea :public AbstractDrink{ public : virtual void Boil () { cout<<"煮水" <<endl; } virtual void Brew () { cout<<"冲泡奶茶" <<endl; } virtual void PourInCup () { cout<<"放入杯子" <<endl; } virtual void PutSomething () { cout<<"加糖加奶" <<endl; } }; void doWork (AbstractDrink *abs) { abs->Make (); delete abs; } void test01 () { doWork (new Coffee); cout<<"--------------" <<endl; doWork (new Milktea); } int main () { test01 (); return 0 ; }
虚析构和纯虚析构 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名(){}=0
类名::~类名(){}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Animal { public : Animal () { cout<<"Animal的构造函数" <<endl; } virtual void speak () =0 ; virtual ~Animal () { cout<<"Animal的析构函数" <<endl; } }; class Cat :public Animal{ public : Cat (string name) { cout<<"Cat的构造函数" <<endl; Cat_name = new string (name); } virtual void speak () { cout<<*Cat_name<<"小猫喵喵瞄" <<endl; } ~Cat () { if (Cat_name != NULL ) { cout<<"Cat的析构函数" <<endl; delete Cat_name; Cat_name = NULL ; } } string *Cat_name; }; int main () { Animal *cat = new Cat ("Tom" ); cat->speak (); delete cat; return 0 ; }
多态案例三-电脑组装需求分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 class CPU { public : virtual void calculate () =0 ; }; class Video { public : virtual void display () =0 ; }; class Memory { public : virtual void storage () =0 ; }; class Computer { public : Computer (CPU *cpu, Video *video, Memory *mem) { m_cpu = cpu; m_cv = video; m_mem = mem; } void doWork () { m_cpu->calculate (); m_cv->display (); m_mem->storage (); } ~Computer () { if (m_cpu != NULL ) { delete m_cpu; m_cpu = NULL ; } if (m_cv != NULL ) { delete m_cv; m_cv = NULL ; } if (m_mem != NULL ) { delete m_mem; m_mem = NULL ; } } private : CPU *m_cpu; Video * m_cv; Memory * m_mem; }; class IntelCPU :public CPU{ public : virtual void calculate () { cout<<"Intel的CPU开始工作" <<endl; } }; class IntelVideo :public Video{ public : virtual void display () { cout<<"Intel的Video开始工作" <<endl; } }; class IntelMemory :public Memory{ public : virtual void storage () { cout<<"Intel的Memory开始工作" <<endl; } }; class LenovoCPU :public CPU{ public : virtual void calculate () { cout<<"Lenovo的CPU开始工作" <<endl; } }; class LenovoVideo :public Video{ public : virtual void display () { cout<<"Lenovo的Video开始工作" <<endl; } }; class LenovoMemory :public Memory{ public : virtual void storage () { cout<<"Lenovo的Memory开始工作" <<endl; } }; void test01 () { CPU * intelcpu = new IntelCPU; Video * intelvideo = new IntelVideo; Memory * intelmem = new IntelMemory; cout<<"-------组装第一台--------" <<endl; Computer * computer1 = new Computer (intelcpu, intelvideo, intelmem); computer1->doWork (); delete computer1; cout<<"-------组装第二台--------" <<endl; Computer * computer2 = new Computer (new LenovoCPU, new LenovoVideo, new LenovoMemory); computer2->doWork (); delete computer2; cout<<"-------组装第三台--------" <<endl; Computer * computer3 = new Computer (new LenovoCPU, new IntelVideo, new LenovoMemory); computer3->doWork (); delete computer3; } int main () { test01 (); return 0 ; }