C++基础
本文收录了黑马程序员C++网课的部分代码,可供参考。
指针
指针的基本概念
//可以通过指针间接访问内存
//内存编号是从0开始记录的 一般会用十六进制数字表示
//可以利用指针变量保存地址
//语法:数据类型*变量名;
#include<iostream>
using namespace std;
int main() {
//定义指针
int a = 10;
int * p1;
//让指针记录变量a的地址
p1= &a;//&为取址符号
cout << "a的地址为:" << &a << endl;
cout << "指针p为:" << p1<<endl;
//使用指针
//可以通过解引用的方式来找到指针指向的内存
//指针前加*代表解引用 找到指针指向的内存中的数据
*p1 = 1000;
cout << "a=" << a << endl;
cout << "*p=" << *p1 << endl;
system("pause");
return 0;
}
指针所占内存空间
//在32位操作系统下 占用4个字节空间 64位操作系统下 占用8个字节空间
#include<iostream>
using namespace std;
int main() {
int a = 10;
/*int* p;
p = &a;*/
int* p2 = &a;//创建指针
// 以下全部打印 8
cout << "sizeof int * =" << sizeof(int*) << endl;
cout << "sizeof float * =" << sizeof(float*) << endl;
cout << "sizeof double * =" << sizeof(double*) << endl;
cout << "sizeof char * =" << sizeof(char*) << endl;
system("pause");
return 0;
}
空指针
//空指针:指针变量指向内存中编号为0的空间
//用途:初始化指针变量
//注意:空指针指向的内存是不可以访问的
#include<iostream>
using namespace std;
int main() {
//空指针
//用于给指针变量进行初始化且不可以进行访问
//0~255之间的内存编号是系统占用的 因此不可以访问
int* p3=NULL;
*p3 = 100; //不可访问,强行运行会报错
system("pause");
return 0;
}
野指针
//指针变量指向非法的内存空间
#include<iostream>
using namespace std;
int main() {
//野指针
//在程序中 尽量避免出现野指针
int* p4 = (int*)0x1100;
cout << *p4 << endl; //强行访问会出错
system("pause");
return 0;
}
//空指针和野指针都不是我们申请的空间 因此不要访问 访问就会出错
const修饰指针
/*const修饰指针有三种情况
const修饰指针——常量(的)指针 指针的指向(p=&x)可以修改 但是指针指向的值(指向的内存的数据)(*p)不可以改
(const int * p=&a;)
const修饰常量——指针(是)常量 指针的指向不可以修改 但是指针指向的值可以改
(int * const p=&a;)
const既修饰指针 又修饰常量 指针的指向和指向的值都不可以改
(const int * const p=&a;)
*/
#include<iostream>
using namespace std;
int main() {
////const修饰指针
//int a = 10;
//int b = 10;
//int* p= &a;
//const int* p = &a;//常量指针 *p指针指向的值不能改 指针指向可以改
//p = &b;
////const修饰常量
//int* p = &a;
//int* const p = &a;//指针指向不可以改 指针指向的值*p可以改
//*p = 100;
////const修饰指针和常量
//int* p = &a;
////指针指向和指针指向的值都不可以改
system("pause");
return 0;
}
程序的内存模型
程序执行时,将内存大方向分为4个区域:
代码区:存放函数体的二进制代码,有操作系统进行管理。
全局区:存放全局变量和静态变量以及常量(字符串常量,const修饰的全局变量)。
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
引用
引用做函数返回值
//注意不要返回局部变量引用
//用法:函数调用作为左值
#include<iostream>
using namespace std;
int& test01() {
int a = 10;//存放在栈区,test01执行后就被释放掉了
return a;
}
int& test02() {
static int a = 10;//静态变量存放在全局区,全局区上的数据在程序结束后系统释放
return a;
}
int main() {
int& ref = test02();//这里ref可以理解为test02的别名,且test02是a的别名
cout << ref << endl; //10
test02() = 100;//函数调用作为左值
cout << ref << endl; //100
return 0;
}
引用的本质
引用的本质是在c++中实现一个指针常量。int *const p; p=&x
不可以更改,*p
可以更改。
所以引用一旦初始化之后,就不可更改。
int &ref = a; ---> int *const p = &a;
ref = 20 ---> *p = 20;
常量引用
//常量引用主要用来修饰形参,防止误操作
//在函数形参列表中,可以加const修饰形参,防止形参改变实参
#include<iostream>
using namespace std;
void showValue(const int& val) {
//val = 1000;
cout << "val=" << val << endl;
}
int main() {
/* int a = 10;
//int& ref = 10;这行代码是错误的
const int& ref = 10;//加上const之后,编译器将代码修改为 int temp =10; const int& ref = temp;
//加入const之后ref不可修改了 */
int a = 100;
showValue(a);
cout << a << endl;
return 0;
}
函数高级
函数的默认参数
//某一个位置有默认值,则从这个位置开始,从左往右,都必须有默认值
//如果函数声明有默认值,那么函数实现的时候就不能有默认值
#include<iostream>
using namespace std;
int func(int a, int b=20, int c=30) {
return a + b + c;
}
int func01(int a, int b=10);
int func01(int a, int b) {//声明和实现只能有一个地方有默认值
return a + b;
}
int main() {
int a = 10,d=0;
d = func(a,40);//自己传入数据就用自己的,否则就用默认的
cout << d << endl;
return 0;
}
函数的占位参数
#include<iostream>
using namespace std;
void func(int a,int =10) {//后面的int起到占位的作用,占位参数后面也可以跟一个数据
cout << "this is function" << endl;
}
int main2() {
func(1,2);
return 0;
}
函数重载的概述
//函数名可以相同,提高复用性
//函数重载满足条件如下:
//同一个作用域下,函数名称相同,函数参数类型不同或者个数不同或者顺序不同
#include<iostream>
using namespace std;
//同一作用域下,函数名称相同
void func() {
cout << "func的调用" << endl;
}
void func(int a) {//参数类型或者个数不同
cout << "func(int a)的调用" << endl;
}
void func(double a) {
cout << "func(double a)的调用" << endl;
}
void func(double a,int b) {//参数类型顺序不同
cout << "func(double a,int b)的调用" << endl;
}
void func(int b,double a) {
cout << "func(int b,double a)的调用" << endl;
}
int main3() {
func();
func(10);
func(3.14);
func(3.14, 10);
func(10, 3.14);
//函数的返回值不可以作为函数承载的返回值条件
return 0;
}
函数重载的注意事项
//引用作为重载条件;函数重载碰到函数默认参数
#include<iostream>
using namespace std;
void func(const int &a) {
cout << "func(const int &a)的调用" << endl;
}
void func(int& a) {//int &a=10;这个语句不合法,所以func(10)走上面的函数
cout << "func(int &a)的调用" << endl;
}
void func2(int a, int b=10) {
cout << "func(int a,int b)的调用" << endl;
}
void func2(int a) {
cout << "func(int a)的调用" << endl;
}
int main() {
int a = 10;
func(a);//调用可读可写的函数
func(10);//调用const
func2(a,20);
return 0;
}
文件操作
写文件
//ofstream写操作-output
//ifstream读操作-input
//fstream读写操作
#include<iostream>
#include<fstream>
using namespace std;
void test01() {
ofstream ofs;
ofs.open("text.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
int main() {
test01();
return 0;
}
读文件
#include<iostream>
#include<fstream>
#include<string>
//EOF--->end of file
using namespace std;
void test01() {
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open()) {
cout << "error" << endl;
return;
}
/*char buf[1024] = { 0 };
while (ifs >> buf) {
cout << buf << endl;
}
ifs.close();*/
/*char buf[1024] = { 0 };
while (ifs.getline(buf, sizeof(buf))) {
cout << buf << endl;
}
ifs.close();*/
string buf;
while (getline(ifs, buf)) {//需要用循环来获取数值
cout << buf << endl;
}
ifs.close();
}
int main() {
test01();
return 0;
}
二进制文件
#include<iostream>
#include<fstream>
//二进制文件使用write进行写文件操作
using namespace std;
struct Person {
char m_Name[64];
int m_Age;
};
void test01() {
ofstream ofs("person.txt", ios::out | ios::binary);
Person p = { "张三",18 };
ofs.write((const char*)&p, sizeof(Person));//数据的地址以const char* 的方式传入,sizeof体现出要写多长
ofs.close();
}
void test02() {
ifstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "error" << endl;
return;
}
else {
Person p;
ifs.read((char*)&p, sizeof(p));
cout << "姓名:" << p.m_Name << endl;
cout << "年龄:" << p.m_Age << endl;
ifs.close();
}
}
int main() {
//test01();
test02();
return 0;
}
类和对象
友元
全局函数做友元
//有些私有属性也想让类外的函数或类来访问,关键字为friend
#include<iostream>
#include<string>
using namespace std;
class Building {
//友元写到类里面就彳亍
friend void goodGay(Building& building);//让goodGay是Building的好朋友,可以访问私有成员
public:
Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom;//客厅
private:
string m_BedRoom;//卧室
};
//全局函数
void goodGay(Building &building) {
cout << "好基友 正在访问:" << building.m_SittingRoom << endl;
cout << "好基友 正在访问:" << building.m_BedRoom << endl;
}
int main() {
Building building;
goodGay(building);
return 0;
}
友元类
#include<iostream>
using namespace std;
class Building02 {
friend class goodGay02;//goodGay可以访问本类私有的成员
public:
Building02();
public:
string m_Sittingroom;
private:
string m_Bedroom;
};
class goodGay02 {
public:
goodGay02();
public:
Building02* building;
void visit();//参观函数,访问building中的属性
};
//类外写成员函数
Building02::Building02() {
m_Sittingroom = "客厅";
m_Bedroom = "卧室";
}
goodGay02::goodGay02() {
//创建建筑物对象
building = new Building02;
}
void goodGay02::visit() {
cout << "好基友正在访问:" << building->m_Sittingroom << endl;
cout << "好基友正在访问:" << building->m_Bedroom << endl;
}
void test01(){
goodGay02 gg;
gg.visit();
}
int main() {
test01();
return 0;
}
成员函数做友元
#include<iostream>
using namespace std;
class Building03;
class goodgay {
public:
goodgay();
void visit();//让visit可以访问building中的私有成员
void visit02();
Building03* building;
};
class Building03 {
friend void goodgay::visit();//告诉编译器,这是一个goodgay下的visit函数作为友元
public:
Building03();
public:
string sittingroom;
private:
string bedroom;
};
//类外实现成员函数
Building03::Building03() {
sittingroom = "客厅";
bedroom = "卧室";
}
goodgay::goodgay() {
building = new Building03;
}
void goodgay::visit() {
cout << "visit正在访问:" << building->sittingroom << endl;
cout << "visit正在访问:" << building->bedroom << endl;
}
void goodgay::visit02() {
cout << "visit02正在访问:" << building->sittingroom << endl;
//cout << "visit02正在访问:" << building->bedroom << endl; 访问不了
}
void test01() {
goodgay gg;
gg.visit();
gg.visit02();
}
int main() {
test01();
return 0;
}
对象特性
深拷贝和浅拷贝
#include<iostream>
using namespace std;
//浅拷贝:简单的赋值操作
//深拷贝:在堆区重新申请空间,进行拷贝操作
class Person05 {
public:
Person05() {
cout << "默认构造函数调用" << endl;
}
Person05(int age,int height) {
m_Age = age;
m_Height=new int(height);//new int返回的是int*
cout << "有参构造函数调用" << endl;
}
Person05(const Person05 &p) {
m_Age = p.m_Age;
m_Height = new int(*p.m_Height);//深拷贝
}
~Person05() {
//析构将在堆区的数据释放
if (m_Height != NULL) {
delete m_Height;
m_Height = NULL;//防止出现野指针
}
cout << "析构函数调用" << endl;
}
int m_Age;
int* m_Height;
};
void test01() {
Person05 p1(18,160);
cout << "p1的年龄为:" << p1.m_Age << endl;
cout << "p1的身高为:" << *p1.m_Height << endl;
Person05 p2(p1);//浅拷贝
cout << "p2的年龄为:" << p2.m_Age << endl;
cout << "p2的身高为:" << *p2.m_Height << endl;
}
int main() {
test01();
return 0;
}
初始化列表
//构造函数:属性1(值1),属性2(值2)......{ }
#include<iostream>
using namespace std;
class Person06 {
public:
//传统初始化
/*Person06(int a,int b,int c) {
m_A = a;
m_B = b;
m_C = c;
}*/
//初始化列表初始化属性
Person06(int a,int b,int c) :m_A(a), m_B(b), m_C(c){
}
int m_A;
int m_B;
int m_C;
};
void test01() {
Person06 p(10,20,30);
cout << p.m_A << ' ' << p.m_B << ' ' << p.m_C << endl;
}
int main() {
test01();
return 0;
}
静态成员变量
//静态成员变量:所有对象共享同一份数据,在编译阶段分配内存,类内声明,类外初始化
//静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;
class Person08 {
public:
//类内声明
static int m_A;
//静态成员变量也是有访问权限的
private:
static int m_B;
};
int Person08::m_A=100;//类外初始化
int Person08::m_B = 200;
void test01() {
Person08 p;
cout << p.m_A << endl;
Person08 p2;
p2.m_A = 200;//编译阶段共享同一份数据
cout << p.m_A << endl;
}
void test02() {
//静态成员变量,不属于某个对象上,所有对象共享同一份数据
//可以通过对象或者类名进行访问
Person08 p;
cout << p.m_A << endl;//对象访问
cout << Person08::m_A << endl;//类名访问
//cout << Person08::m_B << endl;
//无法访问,因为静态也是有私有权限的
}
int main() {
//test01();
test02();
return 0;
}
静态成员函数
//静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;
class Person09 {
public:
static void func() {
//m_B = 200; 静态成员函数不可以访问非静态成员
m_A = 100;//静态成员函数可以访问静态成员变量
cout << "static void func调用" << endl;
}
static int m_A;
int m_B;
private://类外无法访问私有的静态成员函数
static void func02() {
cout << "func02的调用" << endl;
}
};
int Person09::m_A = 0;
void test01() {
//通过对象访问
Person09 p;
p.func();
//通过类名访问
Person09::func();
}
int main() {
test01();
return 0;
}
空指针访问成员函数
#include<iostream>
using namespace std;
class Person12 {
public:
void showClassName() {
cout << "this is Person class" << endl;
}
void showPersonAge() {
if (this == NULL) {
return;
}
cout << "age=" << this->m_Age << endl;
}
int m_Age;
};
void test01() {
Person12* p = NULL;
p->showClassName();
p->showPersonAge();
}
int main() {
test01();
return 0;
}
const 修饰成员函数
/成员函数后加const后我们称为常函数
//常函数内不可以修改成员属性
//加上mutable后,常函数中依旧可以修改
#include<iostream>
using namespace std;
class Person13 {
public:
void showPerson() const{
//m_A = 100;错误的,因为此时m_A不可以更改
m_B = 100;//正确的,因为m_B前面有mutable
//隐含在每一个成员函数的内部都有一个this指针
//this指针是指针常量,指向不能改
//加入const后,指针变成了const Person* const this;
}
void func() {
m_A = 100;
}
int m_A;
int mutable m_B;
};
//常对象
void test01() {
const Person13 p;//常对象,不允许修改值
//p.m_A = 100; 错误的,因为修改不了
p.m_B = 100; //正确的,因为有mutable
//常对象只能调用常函数
p.showPerson();
//p.func(); 错误的,因为func不是常函数
}
继承
继承中的对象模型
在继承中,父类中所有的非私有成员属性都会被子类继承下去,而父类中私有的成员属性是被编译器给隐藏了,所以访问不到,但是确实被继承下去了。
继承中同名成员的处理方式
//访问子类同名成员,直接访问即可
//访问父类同名成员,需要加作用域
//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名的成员函数(包括函数的重载)
#include<iostream>
using namespace std;
//继承中同名成员处理
class Base05 {
public:
int m_A;
Base05() {
m_A = 100;
}
void func() {
cout << "Base - func调用" << endl;
}
void func(int a) {
cout << "Base - func(int a)调用" << endl;
}
};
class Son05 :public Base05 {
public:
Son05() {
m_A = 200;
}
int m_A;
void func() {
cout << "Son - func调用" << endl;
}
};
void test01() {
Son05 s;//出现同名直接访问则访问自身
cout << "m_A = " << s.m_A << endl;
//访问base下的m_A需要加上作用域
cout << "m_A = " << s.Base05::m_A << endl;
}
//同名函数处理
void test02() {
Son05 s;
s.func();//直接调用的调用的是子类中的成员
s.Base05::func();//加上作用域就可以访问到父类的成员
s.Base05::func(1);//同样也需要加上作用域
}
int main() {
//test01();
test02();
return 0;
}
继承中同名静态成员的处理方式
//静态成员类内声明类外初始化,所有对象共享一份数据,编译阶段便分配内存
//静态成员函数只能访问静态成员变量
//可以通过对象访问,也可以通过类名访问
#include<iostream>
using namespace std;
//继承中的同名静态成员处理方式
class Base06 {
public:
static int m_A;
static void func() {
cout << "Base-static void func()" << endl;
}
};
int Base06::m_A = 100;
class Son06 :public Base06 {
public:
static int m_A;
static void func() {
cout << "Son-static void func()" << endl;
}
};
int Son06::m_A = 200;
void test01() {
//静态同名成员属性
//通过对象访问
Son06 s;
cout << "Son-m_A = " << s.m_A << endl;
cout << "Base-m_A = " << s.Base06::m_A << endl;//同样要加作用域
//通过类名访问
cout << "类名访问" << endl;
cout << "Son-m_A = " << Son06::m_A << endl;
cout << "Base-m_A = " << Son06::Base06::m_A << endl;//第一个::代表通过类名方式访问,第二个::代表通过父类作用域下访问
}
void test02() {
//同名静态成员函数
Son06 s;
s.func();
s.Base06::func();//通过对象访问
Son06::func();
Base06::func();//通过类名访问
}
int main() {
//test01();
test02();
return 0;
}
多态
多态基本语法
//多态分为静态多态(函数,运算符重载)和动态多态(派生类和虚函数)
//动态多态的函数地址在运行阶段再确定其地址
#include<iostream>
using namespace std;
//多态
//动物类
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();
}
void test01() {
Cat cat;
doSpeak(cat);
//父类的引用指向子类对象,C++允许父类的引用可以和子类进行转换
Dog dog;
doSpeak(dog);
cout << sizeof(Animal) << endl;
//virtual 的时候会生成一个指针vfptr虚函数指针,子类通过继承覆盖指针的地址来重写函数
}
int main() {
//动态多态满足条件:
//1、有继承条件
//2、子类重写父类的虚函数
//重写:函数返回值类型 函数名 参数列表要完全相同
//动态多态的使用
//父类的指针或者引用来指向子类对象
//Animal& animal = cat ;
test01();
return 0;
}
纯虚函数和抽象类
//多态中,父类的虚函数的实现是毫无意义的,主要都是调用子类重写的内容
//因此可以将虚函数改为纯虚函数
//抽象类的子类必须重写父类的纯虚函数,否则子类也是抽象类
//语法:virtual 返回值类型 函数名(参数列表)= 0 ;
//当类中有了纯虚函数,这个类也叫抽象类,无法实例化对象
#include<iostream>
using namespace std;
class Base {
public:
virtual void func() = 0;//纯虚函数
//这个类只要有一个纯虚函数,这个类叫做抽象类,无法实例化对象
};
class Son :public Base {
public:
void func() {
cout << "纯虚函数的重写" << endl;
}
};
void test01() {
Base* base = new Son;
base->func();
}
int main() {
test01();
return 0;
}
虚析构和纯虚析构
//多态使用的时候,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
//解决方式:将父类中的析构函数改为虚析构或者纯虚析构
#include<iostream>
using namespace std;
class Animal05 {
public:
Animal05() {
cout << "Animal的构造函数" << endl;
}
/*virtual ~Animal05() {
cout << "Animal的析构函数" << endl;
}*/
virtual ~Animal05() = 0;//纯虚析构
//纯虚函数
virtual void speak() = 0;
};
//纯虚析构,需要类内声明类外定义
//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
Animal05::~Animal05() {
cout << "Animal的纯虚析构函数" << endl;
}
//父类的纯虚函数子类必须重写,否则子类也是抽象类
class Cat05 : public Animal05 {
public:
Cat05(string name) {
cout << "Cat的构造函数调用" << endl;
m_Name = new string(name);
}
virtual void speak() {
cout <<*m_Name<< "小猫在说话" << endl;
}
~Cat05() {
if (m_Name != NULL) {
cout << "Cat的析构函数调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string* m_Name;
};
void test01() {
Animal05* animal = new Cat05("Tom");
animal->speak();
//父类指针在析构的时候,不会调用子类的析构函数,导致子类如果有堆区的数据,会出现内存泄漏
//利用虚析构可以解决父类指针释放子类对象不干净的问题
delete animal;
}
int main() {
test01();
return 0;
}
案例-电脑组装需求
//电脑组装需要CPU,显卡,内存条
//将每个零件封装出抽象基类,并且提供不同的厂商生产的不同的零件
//创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
//测试时组装三台不同的电脑进行工作
#include<iostream>
using namespace std;
//先写出每个零件的抽象类,然后再让具体的厂商去继承零件的抽象类做具体实现
//抽象CPU类
class CPU {
public:
virtual void calculate() = 0;
};
//抽象显卡类
class VideoCard {
public:
virtual void display() = 0;
};
//抽象内存条类
class Memory {
public:
virtual void storage() = 0;
};
//电脑类
class Computer {
public:
Computer(CPU* cpu, VideoCard* vc, Memory* mem) {
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
//提供工作函数
void Work() {
//调用零件工作接口
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
//析构函数释放堆区数据
~Computer() {
if (m_cpu != NULL) {
delete m_cpu;
m_cpu = NULL;
}
if (m_vc != NULL) {
delete m_vc;
m_vc = NULL;
}
if (m_mem != NULL) {
delete m_mem;
m_mem = NULL;
}
}
private:
CPU* m_cpu;//创建cpu零件指针
VideoCard* m_vc;//显卡指针
Memory* m_mem;//内存条指针
};
//具体厂商
//Intel 厂商
class IntelCpu :public CPU {
public:
void calculate() {
cout << "Intel的cpu开始计算" << endl;
}
};
class IntelVideoCard :public VideoCard {
public:
void display() {
cout << "Intel的显卡开始显示" << endl;
}
};
class IntelMemory :public Memory {
public:
void storage() {
cout << "Intel的内存条开始存储了" << endl;
}
};
//Lenovo 厂商
class LenovoCpu :public CPU {
public:
void calculate() {
cout << "Lenovo的cpu开始计算" << endl;
}
};
class LenovoVideoCard :public VideoCard {
public:
void display() {
cout << "Lenovo的显卡开始显示" << endl;
}
};
class LenovoMemory :public Memory {
public:
void storage() {
cout << "Lenovo的内存条开始存储了" << endl;
}
};
//开始测试组装
void test01() {
//电脑零件
CPU* intelCpu = new IntelCpu;
VideoCard* intelCard = new IntelVideoCard;
Memory* intelMem = new IntelMemory;
//第一台电脑
Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
computer1->Work();
delete computer1;
}
int main() {
test01();
return 0;
}
运算符重载
加号运算符重载
//对运算符进行重载,赋予其另一种功能
#include<iostream>
using namespace std;
class Person {
public:
//成员函数重载+号
//Person operator+(Person& p) {
// Person temp;
// temp.m_A = this->m_A + p.m_A;
// temp.m_B = this->m_B + p.m_B;
// return temp;
//}
int m_A;
int m_B;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载实现Person+int
Person operator+(Person& p1, int num) {
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test01() {
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
cout << "p3.m_A" << ' ' << p3.m_A << endl;
cout << "p3.m_B" << ' ' << p3.m_B << endl;
//运算符重载也可以发生函数重载
}
int main() {
test01();
return 0;
}
递增运算符重载
#include<iostream>
using namespace std;
//重载递增运算符
//自定义整型
class myInt {
public:
friend ostream& operator<<(ostream& cout, myInt myint);
myInt() {
m_Num = 0;
}
//重载++运算符(前置递增)
myInt operator++() {//返回引用可以让我们一直对一个数据进行操作
m_Num++;
return *this;
}
//重载++运算符(后置递增)
//这个int代表占位参数,用于区分前置和后置递增
myInt operator++(int) {
//先返回值,再做运算
//先 记录当时结果
myInt temp = *this;
//后 递增
m_Num++;
//最后将记录的结果进行返回
return temp;//返回的是局部对象的引用,如果直接返回引用,就会出问题
}
private:
int m_Num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, myInt myint) {
cout << myint.m_Num;
return cout;
}
void test01() {
myInt myint;
//cout <<++myint << endl;//需要先重载左移运算符
cout << myint.operator++() << endl;
}
void test02() {
myInt myint;
cout << myint++<< endl;
cout << myint .operator++(4)<< endl;
}
int main() {
//test01();
test02();
/*int a = 1;
cout <<a++<< endl;
cout << a << endl;*/
return 0;
}
左移运算符重载
//重载左移运算符可以输出自定义的数据类型
#include<iostream>
using namespace std;
//左移运算符重载
class Person03 {
public:
//不会利用成员函数重载左移运算符,因为无法实现cout在左侧
//只能利用全局函数重载左移运算符
int m_A;
int m_B;
};
ostream& operator<<(ostream &cout,Person03 p) {//本质:operator<<(cout,p) 简化为cout<<p
cout << p.m_A << ' ' << p.m_B ;
return cout;//链式编程思想
}
void test01() {
Person03 p;
p.m_A = 10;
p.m_B = 10;
cout << p<<endl;
}
int main() {
test01();
return 0;
}
赋值运算符重载
#include<iostream>
using namespace std;
//赋值运算符重载
class Person04 {
public:
Person04(int age) {
m_Age=new int(age);
}
~Person04() {
if (m_Age != NULL) {
delete m_Age;
m_Age = NULL;
}
}
//重载赋值运算符
Person04& operator=(Person04 &p) {
//编译器提供的是浅拷贝
//应该先判断是否有属性在堆区,如果有,就要先释放干净,再深拷贝
if (m_Age != NULL) {
delete m_Age;
m_Age = NULL;
}
m_Age= new int(*p.m_Age);
return *this;
}
int* m_Age;
};
void test01() {
Person04 p1(18);
Person04 p2(20);
Person04 p3(30);
cout << "p1的年龄:" << *p1.m_Age << endl;
cout << "p2的年龄:" << *p2.m_Age << endl;
p3 = p2 = p1;//赋值操作
cout << "p1的年龄:" << *p1.m_Age << endl;
cout << "p2的年龄:" << *p2.m_Age << endl;
}
int main() {
test01();
/*int a = 10;
int b = 20;
int c = 30;
c = b = a;
cout << a << ' ' << b << ' ' << c << endl;*/
return 0;
}
关系运算符重载
#include<iostream>
using namespace std;
class Person05 {
public:
Person05(string name, int age) {
m_Name = name;
m_Age = age;
}
//重载==号
bool operator==(Person05& p) {
if (p.m_Age == this->m_Age && p.m_Name == this->m_Name) {
return true;
}
else {
return false;
}
}
string m_Name;
int m_Age;
};
void test01() {
Person05 p1("Tom", 18);
Person05 p2("Tom", 18);
if (p1 == p2) {
cout << "p1与p2相等" << endl;
}
else {
cout << "p1与p2不相等" << endl;
}
}
int main() {
test01();
return 0;
}
函数调用运算符重载
//函数调用运算符()也可以重载
//由于重载后的使用方式非常像函数的调用,因此称为仿函数
//仿函数没有固定写法,非常灵活
#include<iostream>
using namespace std;
//打印输出类
class MyPrint {
public:
//重载函数调用运算符
void operator()(string test) {
cout << test << endl;
}
};
void test01() {
MyPrint myPrint;
myPrint("hello world");//由于使用起来非常类似于函数调用,因此也叫做仿函数
}
//仿函数非常灵活,没有固定写法
//加法类
class MyAdd {
public:
int operator()(int num1, int num2) {
return num1 + num2;
}
};
void test02() {
MyAdd myadd;
int ans=myadd(100, 100);
cout << ans << endl;
}
int main() {
// test01();
test02();
return 0;
}
函数对象
#include<iostream>
using namespace std;
//重载函数调用操作符(也就是小括号)的类,其对象称为函数对象
//函数对象使用重载的()时,行为类似函数调用,也叫仿函数
//函数对象是一个类,不是一个函数
class MyAdd {
public:
int operator()(int v1, int v2) {
return v1 + v2;
}
};
void test01() {//函数对象使用的时候,可以像普通函数那样调用,可以有参数,也可以有返回值
MyAdd myAdd;//函数对象
cout << myAdd(10, 10) << endl;
}
//函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint {
public:
MyPrint() {
this->count = 0;
}
void operator()(string test) {
cout << test << endl;
this->count++;
}
int count;//内部的自己的状态,用来记录函数调用的次数
};
void test02() {
MyPrint myPrint;
myPrint("hello world!");
cout << "函数调用次数:" << myPrint.count << endl;
}
void doPrint(MyPrint& mp,string& test) {
mp(test);
}
//函数对象可以作为参数传递
void test03() {
MyPrint myPrint;
string test = "hello C++!";
doPrint(myPrint, test);
}
int main() {
//test01();
//test02();
test03();
return 0;
}
内建函数对象
算数仿函数
#include<iostream>
#include<functional>
using namespace std;
//STL内建了一些函数对象——算数仿函数,关系仿函数,逻辑仿函数
//negate取反仿函数 plus加法仿函数
void test01() {
negate<int> n;//模板
cout<<n(50);
}
void test02() {
plus<int> p;
cout << p(10, 20);
}
int main() {
//test01();
test02();
return 0;
}
关系仿函数
#include<iostream>
#include<functional>
#include<vector>
#include<algorithm>
using namespace std;
//greater 大于 equal 等于 less 小于 not_equal_to 不等于
class MyCompare {
public:
bool operator()(int val1, int val2) {
return val1 > val2;
}
};
void test01() {
vector<int> v;
v.push_back(10);
v.push_back(50);
v.push_back(20);
v.push_back(30);
v.push_back(40);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
//greater<int>() 是内建的函数对象,默认会使用less<>{}
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
}
int main() {
test01();
return 0;
}
逻辑仿函数
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
using namespace std;
//逻辑仿函数
//logical_and 与 logical_or 或 logical_not 非
void test01() {
vector<bool> v;
v.push_back(1);
v.push_back(0);
v.push_back(1);
v.push_back(0);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
//利用逻辑非,将容器v搬运到容器v2中,并执行取反操作
vector<bool> v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), v2.end(), logical_not<bool>());
for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
int main() {
test01();
return 0;
}
谓词
一元谓词
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//返回bool类型的仿函数称为谓词( _Pred )
//如果接受一个参数,那么叫一元谓词
//如果接受两个参数,那么叫二元谓词
class GreaterFive {
public:
bool operator()(int val) {
if (val > 5) {
return true;
}
else {
return false;
}
}
};
void test01() {
vector<int> v;
for (int i = 1; i <= 10; i++) {
v.push_back(i);
}
//查找容器中是否有大于5的数字
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());//匿名函数对象
if (it == v.end()) {
cout << "没有找到" << endl;
}
else {
cout << "找到大于5的元素为:" << *it << endl;
}
}
int main() {
test01();
return 0;
}
二元谓词
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class MySort {
public:
bool operator()(int val1, int val2) {
return val1 > val2;
}
};
static void test01() {
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(20);
v.push_back(50);
v.push_back(40);
sort(v.begin(), v.end(), MySort());//匿名对象调用函数
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
}
int main() {
test01();
return 0;
}
模板
函数模板
#include<iostream>
#include<algorithm>
using namespace std;
//两个整型交换
//void swapInt(int& a, int& b) {
// int t = a;
// a = b;
// b = t;
//}
//交换两个浮点型
//void swapDouble(double& a, double& b) {
// double t = a;
// a = b;
// b = t;
//}
template<typename T>//声明一个模板
void swapnum(T& a, T& b) {
T t = a;
a = b;
b = t;
}
void test01() {
int a = 10;
int b = 20;
swapnum(a, b);//自动类型推导
cout << a << ' ' << b << endl;
double c = 4.13;
double d = 3.14;
swapnum<double>(c, d);//显示指定类型
cout << c << ' ' << d << endl;
}
int main() {
test01();
return 0;
}
函数模板的注意事项
#include<iostream>
using namespace std;
//函数模板的注意事项
//自动类型推导,必须推导出一致的数据类型T才可以使用
//模板必须要确定出T的数据类型,才可以使用
template<typename T>
void myswap(T& a, T& b) {
T t = a;
a = b;
b = t;
}
template<typename C>
void func() {//错误的,因为template下面的函数模板必须要用到T
cout << "hello world" << endl;
}
void test01() {
int a = 10;
int b = 20;
myswap(a, b);
cout << "a = " << a << ' ' << "b = " << b << endl;
func<int>();//正确的,因为函数显示指定类型
}
int main() {
test01();
return 0;
}
普通函数与函数模板的区别
#include<iostream>
using namespace std;
//普通函数调用时可以发生自动类型转换(隐式类型转换)
//函数模板调用时,若利用自动类型推导,不会发生隐式类型转换
//如果利用显示指定类型的方式,可以发生隐式类型转换
//普通函数
int myAdd01(int a,int b) {
return a + b;
}
//函数模板
template<typename T>
T myAdd02(T a, T b) {
return a + b;
}
void test01() {
int a = 10;
double b = 3.14;
cout << myAdd01(a, b) << endl;
}
void test02() {
int a = 10;
int b = 20;
char c = 'a';
cout << myAdd02(a, b) << endl;
//cout << myAdd02(a, c) << endl; 错误的,因为函数模板没有办法进行隐式类型转换
cout << myAdd02<int>(a, c) << endl;//正确的,因为显式指定类型可以进行隐式类型转换
}
int main() {
//test01();
test02();
return 0;
}
普通函数与函数模板的调用规则
#include<iostream>
using namespace std;
//如果函数模板和普通函数都可以实现,优先调用普通函数
//可以通过空模板参数列表来强制调用函数模板
//函数模板也可以发生重载
//如果函数可以产生更好的匹配,优先调用函数模板
void myPrint(int a, int b) {
cout << "调用普通函数" << endl;
}
template<typename T>
void myPrint(T a, T b) {//函数重载
cout << "调用模板函数" << endl;
}
template<typename T>
void myPrint(T a, T b,T c) {//重载函数模板
cout << "调用重载的模板函数" << endl;
}
void test01() {
int a = 10;
int b = 20;
myPrint(a, b);//优先调用普通函数
//通过空模板参数列表,强制调用函数模板
myPrint<>(a, b);
myPrint(a, b, 100);
//函数模板可以更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
myPrint(c1, c2);
}
//既然提供了函数模板,就不要提供了普通函数了,防止出现二义性
int main() {
test01();
return 0;
}
模板的局限性
#include<iostream>
using namespace std;
//模板的局限性
//模板并不是万能的,有些特定的数据类型需要具体化方式来实现
class Person {
public:
Person(string name,int age) {
this->m_Age = age;
this->m_Name = name;
}
/*bool operator==(Person p) {
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {
return true;
}
else {
return false;
}
}*/
string m_Name;
int m_Age;
};
//对比两个数据是否相等的函数
template<typename T>
bool myCompare(T& a, T& b) {
if (a == b) {
return true;
}
return false;
}
//利用具体化的Person的版本来实现代码,具体化优先调用
template<>bool myCompare(Person& p1, Person& p2) {
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) {
return true;
}
else {
return false;
}
}
void test01() {
int a = 10;
int b = 20;
bool ret = myCompare(a, b);
if (ret) {
cout << "a==b" << endl;
}
else {
cout << "a!=b" << endl;
}
}
void test02() {
Person p1("Tom", 10);
Person p2("Tom", 10);
bool ret = myCompare(p1, p2);
if (ret) {
cout << "p1==p2" << endl;
}
else {
cout << "p1!=p2" << endl;
}
}
int main() {
//test01();
test02();
return 0;
}
类模板的语法
#include<iostream>
#include<vector>
using namespace std;
//类模板
template<class NameType,class AgeType>//两种不同类型的模板
class Person07 {
public:
Person07(NameType name,AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << this->m_Name << endl;
cout << "age:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01() {
Person07<string, int>p1("sunwukong", 999);
p1.showPerson();
}
int main() {
test01();
return 0;
}
类模板与函数模板的区别
#include<iostream>
using namespace std;
//类模板没有自动类型推导
//类模板在模板参数列表中可以有默认参数class T=int 该语法只能在类模板中使用
template<class NameType,class AgeType=int>//AgeType默认是整型
class Person08 {
public:
Person08(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "age:" << this->m_Age << endl;
cout << "name:" << this->m_Name << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01() {
//Person08 p("sunwukong", 999); 错误的,无法用自动类型推导
Person08<string, int> p("sumwukong", 999);
p.showPerson();
}
void test02() {
Person08<string, int>p("zhubajie", 999);
}
int main() {
test01();
return 0;
}
类模板中的成员函数创建时机
//普通类中的成员函数一开始就可以创建
//类模板中的成员函数在调用的时候才创建
#include<iostream>
using namespace std;
//类模板中成员函数创建时机
class Person1 {
public:
void showPerson1() {
cout << "Person1 show" << endl;
}
};
class Person2 {
public:
void showPerson1() {
cout << "Person2 show" << endl;
}
};
template<class T>
class myClass {
public:
T obj;//虚拟成员
//类模板中的成员函数
//没调用的时候不会创建
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
void test1() {
myClass<Person1> m;
m.func1();
//m.func2();
}
int main() {
test1();
return 0;
}
类模板对象做函数参数
//类模板实例化出对象,向函数传参的方式
//指定传入类型 参数模板化 整个类模板化
#include<iostream>
using namespace std;
template<class T1,class T2>
class Person {
public:
Person(T1 name,T2 age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << m_Name << ' ' << "age:" << m_Age << endl;
}
T1 m_Name;
T2 m_Age;
};
//指定传入类型(较常用)
void printPerson1(Person <string,int>&p) {
p.showPerson();
}
void test01() {
Person<string, int>p("sunwukong", 100);
//p.showPerson();
printPerson1(p);
}
//参数模板化
template<class T1,class T2>
void printPerson2(Person<T1,T2>&p) {
p.showPerson();
cout << "T1:" << typeid(T1).name() << endl;//typeid().name()可以显示出具体的数据类型
cout << "T2:" << typeid(T2).name() << endl;
}
void test02() {
Person<string, int>p("zhubajie", 90);
printPerson2(p);
}
//整个类模板化
template<class T>
void printPerson3(T& p) {
p.showPerson();
cout << "T:" << typeid(T).name() << endl;
}
void test03() {
Person<string, int> p("tangsen", 30);
printPerson3(p);
}
int main() {
//test01();
//test02();
test03();
return 0;
}
类模板和继承
//子类继承的父类是一个类模板的时候,子类在声明时应该指明父类中T的类型
//如果想灵活指定父类中T的类型,子类也需要变为类模板
#include<iostream>
using namespace std;
template<class T>
class Base {
T m;
};
//class Son :public Base {
// 错误的,因为子类继承的时候没有写明父类中T的参数类型
//};
class Son :public Base<int> {
//正确的,这里指定了父类的参数类型是int
};
void test01() {
Son s1;
}
//如果想灵活指定父类中T的类型,子类也要变成模板
template<class T1,class T2>
class Son2 :public Base<T1> {
public:
Son2() {
cout << "T1:" << typeid(T1).name() << endl;
cout << "T2:" << typeid(T2).name() << endl;
}
T2 obj;
};
void test02() {
Son2<int, char>s2;
//int m;
//char obj;
}
int main() {
//test01();
test02();
return 0;
}
类模板成员函数的类外实现
#include<iostream>
using namespace std;
//类模板成员函数类外实现
template<class T1,class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//构造函数的类外实现
template<class T1,class T2>//先声明虚数据类型
Person<T1,T2>::Person(T1 name, T2 age) {//作用域和参数列表都要加虚数据类型
this->m_Age = age;
this->m_Name = name;
}
//成员函数的类外实现
template<class T1,class T2>
void Person<T1,T2>::showPerson() {
cout << "name:" << this->m_Name << "\n" << "age:" << this->m_Age << endl;
}
void test01() {
Person<string, int> p("Tom", 30);
p.showPerson();
}
int main() {
test01();
return 0;
}
类模板与友元
#include<iostream>
using namespace std;
//通过全局函数来打印Person信息
//--------------------------------------------------------------------------------------------//
//全局函数类外实现要先让编译器看到这个类外的代码
template<class T1,class T2>
class Person14;
template<class T1, class T2>
void printPerson2(Person14<T1, T2> p) {
cout << "类外name:" << p.m_Name << endl << "类外age:" << p.m_Age << endl;
}
//--------------------------------------------------------------------------------------------//
// ^
template<class T1,class T2> // |
class Person14 { // |
public: // |
//全局函数类内实现 // |
friend void printPerson(Person14<T1, T2> p) { // |
cout << "name:" << p.m_Name << endl << "age:" << p.m_Age << endl; // |
} // |
// |
//全局函数类外实现 // |
//加空模板的参数列表,不然类内是普通函数声明,类外是模板函数定义 |
friend void printPerson2<>(Person14<T1, T2> p);//--------------------------------
Person14(T1 name,T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test01() {
Person14<string, int>p("Tom", 20);
//printPerson(p);
printPerson2(p);
}
int main() {
test01();
return 0;
}
//建议全局函数做类内实现,用法简单,并且编译器可以直接识别
类模板的分文件编写
person.hpp 文件如下:
#pragma once
#include<iostream>
using namespace std;
template<class T1, class T2>
class Person13 {
public:
Person13(T1 name, T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
Person13<T1, T2>::Person13(T1 name, T2 age) {
this->m_Age = age;
this->m_Name = name;
}
template<class T1, class T2>
void Person13<T1, T2>::showPerson() {
cout << "age:" << this->m_Age << "\n" << "name:" << this->m_Name << endl;
}
main.cpp 文件如下:
//类模板中成员函数创建时机是在调用阶段,导致分文件编写的时候连接不到
//解决:直接包含cpp或者声明和实现写同一个文件里, 后缀名改为.hpp文件
#include<iostream>
using namespace std;
#include"person.hpp"
//template<class T1,class T2>
//class Person {
//public:
// Person(T1 name, T2 age);
// void showPerson();
// T1 m_Name;
// T2 m_Age;
//};
//template<class T1,class T2>
//Person<T1, T2>::Person(T1 name, T2 age) {
// this->m_Age = age;
// this->m_Name = name;
//}
//template<class T1, class T2>
//void Person<T1, T2>::showPerson() {
// cout << "age:" << this->m_Age << "\n" << "name:" << this->m_Name << endl;
//}
void test01() {
Person13<string, int> p("Jerry", 18);
p.showPerson();
}
int main() {
test01();
return 0;
}