程序运行前:
程序运行后:
注:不要返回局部变量的地址。C++中主要利用new在堆区开辟内存,由程序员开辟操作,手动释放可以通过delete关键字来释放。利用new创建的数据,会返回该数据对应类型的指针。
语法:数据类型 &别名 = 原名;
int a = 10;
int& b = a;
b = 20;
变量a可以操纵某块内存,该内存中存放的数值是10。给变量a起一个别名变量b,那么变量b也可以操纵那块内存,它们指向同一块内存。通过给变量b赋值为20,那么变量a的数值也是20。
注:
int a = 10;
int& b = a;
int c = 20;
b = c; // 不是更改引用,而是把c赋值给b。由于a,b指向同一块内存,此时a、b、c都等于20。
形参:函数定义处的占位符。
实参:实际参数,函数调用时,实参会被传递到形参。
#include
using namspace std;
void swap01(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
cout << "swap01中的形参a = " << a << endl;
cout << "swap01中的形参b = " << b << endl;
}
int main()
{
int a = 10;
int b =20;
swap01(a, b) //值传递,形参不会修饰实参
cout << "实参a = " << a << endl;
cout << "实参b = " << b << endl;
return 0;
}
此时输出的形参发生交换a=20,b=10。但实参不改变a=10, b=20。
#include
using namspace std;
void swap02(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b =20;
swap02(&a, &b) //地址传递,形参会修饰实参
cout << "实参a = " << a << endl;
cout << "实参b = " << b << endl;
return 0;
}
此时,实参a=20,实参b=10。
#include
using namspace std;
void swap03(int &a, int &b) // 给a、b起别名a、b,可达到修饰实参的效果,又避免指针的复杂写法
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b =20;
swap03(&a, &b) //引用传递,形参会修饰实参。
cout << "实参a = " << a << endl;
cout << "实参b = " << b << endl;
return 0;
}
此时,实参a=20,实参b=10。
总结:函数传参数时,引用可以让形参修饰实参,可以简化指针修改实参。
注:
不要返回局部变量的引用
函数的调用可以作为左值
#include
int a = 10;
int& test() // 表示返回引用
{
std::cout << "a = " << a << std::endl;
return a;
}
int main()
{
int &ref = test();
a = 20;
std::cout << "ref: " << ref << std::endl;
}
#include
int& test()
{
static int b = 10; // static静态变量存放于全局区,不会在函数执行结束后被释放。
return b;
}
int main()
{
int &ref = test();
std::cout << "ref: " << ref << std::endl;
}
#include
int& test()
{
static int b = 10;
return b;
}
int main()
{
int &ref = test();
std::cout << "ref: " << ref << std::endl;
test() = 1000; // 如果函数的返回值是引用,调用函数可以作为左值使用
std::cout << "ref: " << ref << std::endl;
}
引用的本质在c++内部实现是一个指针常量(int const ref = &a*)。
#include
void func(int& ref)
{
ref = 100;
}
int main()
{
int a = 10;
int& ref = a; //自动转换为 int* const ref = &a;指针常量是指针指向不可改,所以引用不可更改
ref = 20; //内部发现ref是引用,自动转为*ref = 20;
std::cout << "a: " << a << std::endl;
std::cout << "ref: " << ref << std::endl;
func(a);
std::cout << "a: " << a << std::endl;
std::cout << "ref: " << ref << std::endl;
return 0;
}
结论:C++推荐使用引用技术,因为语法方便,引用本质是指针常量,但是所有额指针操作编译器都帮我们做了。
使用场景:用来修饰形参,防止误操作
#include
void showValue(const int &val)
{
//val = 1000; //加上const以后,此时修改val就是不合法的,避免修改原来的a值。
std::cout << "val = " << val << std::endl;
}
int main()
{
// 引用必须引一块合法的内存空间
// 下面这种写法可以操纵这块内存,但无法知道它的原名
// 加上const以后,无法改变ref的值
const int &ref = 10; // 相对于int temp = 10; const int &ref = temp;
int a =10;
showValue(a);
std::cout << "a = " << a << std::endl;
}
如果自己传入数据,就用自己的数据,没有传则用默认值。
int func(int a, int b=20, int c=30)
{
return a+b+c;
}
注意事项:
void func(int a, int) //占位只写数据类型,第二个int为占位所用
{
std::cout << "This is a func." << std::endl;
}
int main()
{
func(10, 1000); // 必须传入第二个参数,才能正确调用
}
void func(int a, int=10)
{
std::cout << "This is a func." << std::endl;
}
int main()
{
func(10);
}
作用:函数名可以相同,提高复用性。
条件:
void func()
{
std::cout << "func的调用" << std::endl;
}
void func(int a)
{
std::cout << "func(int a)的调用" << std::endl;
}
void func(double a)
{
std::cout << "func(double a)的调用" << std::endl;
}
void func(int a, double b)
{
std::cout << "func(int a, double b)的调用" << std::endl;
}
void func(double a, int b)
{
std::cout << "func(double a, int b)的调用" << std::endl;
}
int main()
{
func();
func(10);
func(3.14);
func(10, 3.14);
func(3.14, 10);
return 0;
}
注:函数返回值不一样不可以作为函数重载的条件;
函数重载的注意事项:
void func(int &a)
{
std::cout << "func(int &a)的调用" << std::endl;
}
void func(const int &a)
{
std::cout << "func(const int &a)的调用" << std::endl;
}
调用第一个:因为a为可读可写的变量,所以默认会调第一个
int main()
{
int a =10;
func(a); // 会调用第一个
}
调用第二个:传入参数是一个常量,第一个函数在这种情况是不合法,所以默认会调用第二个
int main()
{
func(10); // 会调用第二个
}
void func(int a, int b)
{
std::cout << "func(int a, int b)的调用" << std::endl;
}
void func(int a)
{
std::cout << "func(int a)的调用" << std::endl;
}
int main()
{
func(10); //会调用第二个
}
例二:函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况。
void func(int a, int b=10)
{
std::cout << "func(int a, int b)的调用" << std::endl;
}
void func(int a)
{
std::cout << "func(int a)的调用" << std::endl;
}
int main()
{
func(10); //会报错
}
c++面向对象的三大特性:封装、继承、多态。c++认为万事万物都皆为对象,对象上有其属性和行为。具有相同性质的对象,可以抽象为类,人属于人类,车属于车类。
(1)将属性和行为作为一个整体,表现生活中的事务
例1:设计一个圆类,求圆的周长
#include
const double PI = 3.14;
class Circle
{
//访问权限
public:
// 属性
int m_r; //半径
// 行为
double calculateZC() //获取圆的周长
{
return 2*PI*m_r; // 圆求周长的公式:2*PI*半径。
}
};
int main()
{
Circle c1; //通过圆类,创建具体的圆(对象)。实例化,即通过一个类创建一个具体的对象。
c1.m_r = 10; //给圆对象的属性进行赋值
std::cout << "圆的周长为:" << c1.calculateZC() << std::endl;
return 0;
}
例2:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号。
#include
#include
using namespace std;
const double PI = 3.14;
class Student
{
public:
// 类中的属性和行为统一称为成员
// 属性:成员属性 或 成员变量
// 行为:成员函数 或 成员方法
string m_name;
int m_id;
void showInfo()
{
std::cout << "该学生的姓名是:" << m_name;
std::cout << " 学号是:" << m_id << std::endl;
}
void SetName(string name)
{
m_name = name;
}
void SetID(int id)
{
m_id = id;
}
};
int main()
{
Student s1;
s1.SetName("张三");
s1.SetID(202401);
s1.showInfo();
Student s2;
s2.m_name = "李四";
s2.m_id = 202402;
s2.showInfo();
return 0;
}
(2)将属性和行为加以权限控制。
访问权限:
#include
using namespace std;
class Person
{
public:
string m_Name;
protected:
string m_Car;
private:
int m_Password;
public:
void func()
{
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 123456;
}
void showInfo()
{
cout << "姓名:" << m_Name << " 车:" << m_Car;
cout << " 密码:" << m_Password << endl;
}
};
int main()
{
Person p1;
p1.func();
p1.showInfo();
p1.m_Name = "李四";
p1.showInfo();
// p1.m_Car = "奔驰"; // protected权限类外无法访问
// p1.m_Password = "123"; // private权限类外无法访问
return 0;
}
c++中struct和class唯一的区别就在于 默认的访问权限不同
#include
using namespace std;
class C1
{
int m_A; //默认权限是私有
};
struct C2
{
int m_A; //默认权限是公共
}
int main()
{
C1 c1;
// c1.m_A = 100; //会发现不可访问
C2 c2;
c2.m_A = 100; //会发现能正常访问
return 0;
}
优点:
例1:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Person
{
public:
void setName(string name) // 设置姓名
{
m_Name = name;
}
string getName() // 获取姓名
{
return m_Name;
}
int getAge() // 获取年龄
{
return m_Age;
}
void setIdol(string idol)
{
m_Idol = idol;
}
private:
string m_Name; // 姓名 可读可写
int m_Age = 18; //年龄 只读
string m_Idol; // 偶像 只写
};
int main()
{
Person p;
p.setName("张三");
cout << "姓名:" << p.getName() << endl;
//p.m_Age = 20;
cout << "年龄:" << p.getAge() << endl;
p.setIdol("小明");
//cout << "偶像:" << p.m_Idol << endl;
return 0;
}
例2:改一下年龄,让年龄也可以写,但范围需要在0-150之间
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Person
{
public:
void setName(string name) // 设置姓名
{
m_Name = name;
}
string getName() // 获取姓名
{
return m_Name;
}
int getAge() // 获取年龄
{
return m_Age;
}
void setAge(int age) // 设置年龄,且范围在0~150
{
if(age < 0 || age > 150)
{
cout << "年龄" << age << "输出有误,赋值失败" << endl;
return;
}
m_Age = age;
}
void setIdol(string idol)
{
m_Idol = idol;
}
private:
string m_Name; // 姓名 可读可写
int m_Age = 18; //年龄 可读可写,但写入范围在0~150之间
string m_Idol; // 偶像 只写
};
int main()
{
Person p;
p.setName("张三");
cout << "姓名:" << p.getName() << endl;
p.setAge(160);
cout << "年龄:" << p.getAge() << endl;
p.setIdol("小明");
//cout << "偶像:" << p.m_Idol << endl;
return 0;
}
练习案例1:设计立方体类
#include
using namespace std;
#include
//立方体类设计
//1.创建立方体类
//2.设计属性
//3.设计行为,获取立方体面积和体积
//4.分别利用全局函数和成员函数,判断两个立方体是否相等
class Cube
{
public:
// 设置长和获取长
void setL(int l)
{
m_L = l;
}
int getL()
{
return m_L;
}
// 设置宽和获取宽
void setW(int w)
{
m_W = w;
}
int getW()
{
return m_H;
}
// 设置高和获取高
void setH(int h)
{
m_H = h;
}
int getH()
{
return m_H;
}
//计算面积
int calS()
{
return 2*m_L*m_W +2*m_W*m_H +2*m_L*m_H;
}
//计算体积
int calV()
{
return m_L*m_W*m_H;
}
//利用成员函数判断两个立方体是否相等
bool isSameByClass(Cube &c)
{
if(m_L == c.getL() && m_W == c.getW() && m_H == c.getH())
{
return true;
}
return false;
}
private:
int m_L;
int m_W;
int m_H;
};
//利用全局函数判断两个立方体是否相等
bool isSame(Cube &c1, Cube &c2)
{
if(c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())
{
return true;
}
return false;
}
int main()
{
Cube c1;
c1.setL(10);
c1.setW(10);
c1.setH(10);
cout << "c1的面积为:" << c1.calS() << endl;
cout << "c1的体积为:" << c1.calV() << endl;
Cube c2;
c2.setL(10);
c2.setW(10);
c2.setH(10);
// 利用全局函数判断
bool ret = isSame(c1, c2);
if(ret)
{
cout << "全局函数判断结果:c1和c2是相等的" << endl;
}
else
{
cout << "全局函数判断结果:c1和c2是不相等的" << endl;
}
// 利用成员函数判断
ret = c1.isSameByClass(c2);
if(ret)
{
cout << "成员函数判断结果:c1和c2是相等的" << endl;
}
else
{
cout << "成员函数判断结果:c1和c2是不相等的" << endl;
}
return 0;
}
练习案例2:点和圆的关系
设计一个圆形类(Circle)和一个点类(Point),计算点和圆的关系
#include
using namespace std;
#include
//点类
class Point
{
public:
//设置和获取x
void setX(int x)
{
m_X = x;
}
int getX()
{
return m_X;
}
//设置和获取y
void setY(int y)
{
m_Y = y;
}
int getY()
{
return m_Y;
}
private:
int m_X;
int m_Y;
};
//圆类
class Circle
{
public:
//设置和获取半径
void setR(int R)
{
m_R = R;
}
int getR()
{
return m_R;
}
//设置和获取圆心
void setCenter(Point center)
{
m_Center = center;
}
Point getCenter()
{
return m_Center;
}
private:
int m_R; //半径
Point m_Center; //圆心。在类中可以让另一个类作为本类的成员
};
//判断点和圆的关系
void isInCircle(Circle &c, Point &p)
{
//计算两点之间距离的平方
int distance =
(c.getCenter().getX() -p.getX()) * (c.getCenter().getX() -p.getX()) +
(c.getCenter().getY() -p.getY()) * (c.getCenter().getY() -p.getY());
//计算半径的平方
int rDistance = c.getR() * c.getR();
//判断关系
if(distance == rDistance)
{
cout << "点在圆上" << endl;
}
else if(distance > rDistance)
{
cout << "点在圆外" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
int main()
{
Point center;
center.setX(10);
center.setY(0);
Circle c;
c.setR(10);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(9);
isInCircle(c, p);
return 0;
}
上面的例子,主要有两个重点:
拆分后的代码:
#ifndef POINT_H //如果头文件没定义过,就执行下面的#define和endif之前的内容
#define POINT_H
#include
using namespace std;
#include
//点类
class Point
{
public:
//设置和获取x
void setX(int x);
int getX();
//设置和获取y
void setY(int y);
int getY();
private:
int m_X;
int m_Y;
};
#endif
#ifndef CIRCLE_H //如果头文件没定义过,就执行下面的#define和endif之前的内容
#define CIRCLE_H
#include
using namespace std;
#include
//圆类
class Circle
{
public:
//设置和获取半径
void setR(int R);
int getR();
//设置和获取圆心
void setCenter(Point center);
Point getCenter();
private:
int m_R; //半径
Point m_Center; //圆心
};
#endif
#include
#include
using namespace std;
#include
//设置和获取x
void Point::setX(int x)
{
m_X = x;
}
int Point::getX()
{
return m_X;
}
//设置和获取y
void Point::setY(int y)
{
m_Y = y;
}
int Point::getY()
{
return m_Y;
}
#include
//设置和获取半径
void Circle::setR(int R)
{
m_R = R;
}
int Circle::getR()
{
return m_R;
}
//设置和获取圆心
void Circle::setCenter(Point center)
{
m_Center = center;
}
Point Circle::getCenter()
{
return m_Center;
}
#include
using namespace std;
#include
#include
#include
//判断点和圆的关系
void isInCircle(Circle &c, Point &p)
{
//计算两点之间距离的平方
int distance =
(c.getCenter().getX() -p.getX()) * (c.getCenter().getX() -p.getX()) +
(c.getCenter().getY() -p.getY()) * (c.getCenter().getY() -p.getY());
//计算半径的平方
int rDistance = c.getR() * c.getR();
//判断关系
if(distance == rDistance)
{
cout << "点在圆上" << endl;
}
else if(distance > rDistance)
{
cout << "点在圆外" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
int main()
{
Point center;
center.setX(10);
center.setY(0);
Circle c;
c.setR(10);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(9);
isInCircle(c, p);
return 0;
}
注:Visual Studio可以直接用这种方式运行,但VScode只是一个代码编辑器,需要自己配置CMakeLists.txt。
(1)文件夹结构
![[Pasted image 20240102164240.png]]
只需要include/circle.h、include/point.h以及src/circle.cpp、src/point.cpp、src/main.cpp和CMakeLists.txt即可。
(2)CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(code)
add_compile_options(-std=c++11)
set(OpenCV_DIR /opt/ros/kinetic/share/Opencv-3.3.1-dev)
find_package(OpenCV REQUIRED)
find_package(Eigen3 REQUIRED)
include_directories(
${PROJECT_SOURCE_DIR}
${EIGEN3_INCLUDE_DIR}
${OpenCV_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/include
)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_executable(HelloWorld src/HelloWorld.cpp)
add_executable(demo src/demo.cpp)
target_link_libraries(demo ${OpenCV_LIBS} ${EIGEN3_LIBS})
add_executable(main src/main.cpp src/point.cpp src/circle.cpp)
(3)运行命令
mkdir build
cd build
cmake ..
make
../bin/main
c++中的面向对象来源于生活,每个对象都会有初始设置以及对象销毁前的清理数据的设置
对象的初始化和清理也是两个非常重要的安全问题
c++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。如果我们不提供构造和析构,编译器会提供默认的构造和析构,但是是空实现。
(1)构造函数语法:类名( ){ }
(2)析构函数语法:~类名( ){ }
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "Person构造函数的调用" << endl;
}
~Person()
{
cout << "Person析构函数的调用" << endl;
}
};
void test1()
{
Person p; //在栈上的数据,test1执行完毕以后,释放这个对象
}
int main()
{
test1(); //这种写法在执行完test1函数之后,就释放对象,执行了析构函数
Person p; //这种写法在return0执行完之后,释放对象,才会执行析构函数
return 0;
}
分类:
#include
using namespace std;
class Person
{
public:
// 构造函数
Person()
{
cout << "Person无参构造函数的调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person有参构造函数的调用" << endl;
}
//拷贝构造函数
Person(const Person &p)
{
age = p.age;
cout << "Person拷贝构造函数的调用" << endl;
}
~Person()
{
cout << "Person析构函数的调用" << endl;
}
int age;
};
void test01()
{
//括号法
Person p1; //默认构造函数调用
Person p2(10); // 有参构造函数调用
Person p3(p2); // 拷贝构造函数调用
cout << "p2的年龄为:" << p2.age << endl;
cout << "p3的年龄为:" << p3.age << endl;
cout << endl;
// //显示法
Person p4;
Person p5 = Person(10);
Person p6 = Person(p5);
Person(10); // 匿名对象(特点:当前行执行结束后,系统会立即回收掉匿名对象)
cout << endl;
// //隐式转换法
Person p7 = 10; //相对于写了Person p4 = Person(10);
Person p8 = p7;
cout << endl;
}
int main()
{
test01();
return 0;
}
![[Pasted image 20240102181135.png]]
注意事项:
Person p1();
会被认为是函数声明。Person(p3);
这种写法会报错,编译器等价了这个代码为Person p3
,就变成了对象声明。。C++中拷贝构造函数调用时机通常三种情况
示例:
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "Person默认构造函数调用" << endl;
}
Person(int age)
{
m_Age = age;
cout << "Person有参构造函数调用" << endl;
}
Person(const Person &p)
{
m_Age = p.m_Age;
cout << "Person拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person析构构造函数调用" << endl;
}
int m_Age;
};
void test01()
{
Person p1(20);
Person p2(p1);
}
void doWork(Person p){}
void test02()
{
Person p;
doWork(p);
}
Person doWork2()
{
Person p1;
cout << (int*)&p1 << endl;
return p1;
}
void test03()
{
Person p = doWork2();
cout << (int*)&p << endl;
}
int main()
{
test01();
cout << endl;
test02();
cout << endl;
test03();
return 0;
}
![[Pasted image 20240107185012.png]]
注:第三种情况没有调用拷贝构造。。。
默认情况下,c++编译器至少给一个类添加3个函数
构造函数调用规则如下:
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "Person的默认构造函数调用" << endl;
}
Person(int age)
{
cout << "Person的有参构造函数调用" << endl;
m_Age = age;
}
Person(const Person &p)
{
cout << "Person的拷贝构造函数调用" << endl;
m_Age = p.m_Age;
}
~Person()
{
cout << "Person的析构构造函数调用" << endl;
}
int m_Age;
};
void test01()
{
//注释拷贝构造函数,对比不注释,来测试本案例
Person p;
p.m_Age = 18;
Person p2(p);
cout << "p2的年龄为:" << p2.m_Age << endl;
}
int main()
{
test01();
return 0;
}
如果把上面的拷贝构造函数注释掉,执行代码,会发现p2的年龄还是18,析构函数仍然执行了两次,但此时就不会打印”Person的拷贝函数调用“了,因为调用的是编译器提供的拷贝构造函数。
函数类型 | 情况1 | 情况2 |
---|---|---|
默认构造 | × | × |
有参构造 | √ | × |
拷贝构造 | √ | √ |
表格总结: |
浅拷贝:简单的赋值操作。
深拷贝:在堆区重新申请空间,进行拷贝操作。
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "Person的默认构造函数调用" << endl;
}
Person(int age, int height)
{
cout << "Person的有参构造函数调用" << endl;
m_Age = age;
m_Hight = new int(height);
}
~Person()
{
//析构代码,将堆区开辟数据做释放操作
if(m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
cout << "Person的析构构造函数调用" << endl;
}
int m_Age;
int *m_Height;
};
void test01()
{
Person p1(18, 160);
cout << "p1的年龄为:" << p1.m_Age << *p1.m_Height << endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.m_Age << *p2.m_Height << endl;
}
int main()
{
test01();
return 0;
}
以上代码会出错,出错原因分析如下:
//自己实现拷贝构造,解决浅拷贝带来的问题
Person(const Person &p)
{
cout << "Person的拷贝构造函数调用" << endl;
m_Age = p.m_Age;
//m_Height = p.m_Height; //编译器默认实现就是这行代码
//深拷贝操作
m_Height = new int(*p.m_Height);
}
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。1415
c++提供了初始化列表语法,用来初始化属性
语法:构造函数(): 属性1(值1), 属性2(值2)...
#include
using namespace std;
class Person
{
public:
Person(int a, int b, int c): m_A(a), m_B(b), m_C(c){}
void PrintPerson()
{
cout << "m_A: " << m_A << endl;
cout << "m_B: " << m_B << endl;
cout << "m_C: " << m_C << endl;
}
private:
int m_A;
int m_B;
int m_C;
};
int main()
{
Person p(30, 20, 10);
p.PrintPerson();
return 0;
}
c++类中的成员可以是另一个类的对象,我们称该成员为对象成员
例如:
class A{}
class B
{
A a;
}
B类中有对象A作为成员,A为对象成员。
那么当创建B对象时,A与B的构造和析构函数的先后顺序是?
#include
using namespace std;
#include
class Phone
{
public:
Phone(string pName)
{
cout << "Phone的构造函数调用" << endl;
m_PName = pName;
}
~Phone()
{
cout << "Phone的析构函数调用" << endl;
}
string m_PName;
};
class Person
{
public:
Person(string name, string pName):m_Name(name), m_Phone(pName)
{
cout << "Person的构造函数调用" << endl;
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
string m_Name;
Phone m_Phone;
};
void test01()
{
Person p("张三", "苹果MAX");
cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}
int main()
{
test01();
return 0;
}
![[Pasted image 20240102211211.png]]
总结:当其他类对象作为本类成员,构造时候先构造类对象,再构造自身。析构的顺序与构造相反。
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
(1)静态成员变量
#include
using namespace std;
#include
class Person
{
public:
static int m_A; // 类内声明
private:
static int m_B;
};
int Person::m_A = 100; //类外初始化
int Person::m_B = 200;
void test01()
{
Person p1;
cout << p1.m_A << endl;
Person p2;
p2.m_A = 200;
cout << p1.m_A << endl;
}
void test02()
{
// 静态成员变量不属于某个对象上,所有对象都共享同一份数据
// 因此静态成员变量有两种访问方式
//1.通过对象访问
Person p;
cout << p.m_A << endl;
//2.通过类名进行访问
cout << Person::m_A << endl;
//cout << Person::m_B << endl; // 类外访问不到私有静态成员变量
}
int main()
{
test01();
cout << endl;
test02();
cout << endl;
return 0;
}
(2)静态成员函数
#include
using namespace std;
#include
class Person
{
public:
static void func()
{
m_A = 100; //静态成员函数可以访问静态成员变量
//m_B = 200; //静态成员函数不可以访问非静态成员变量
cout << "static void func调用" << endl;
}
static int m_A;
int m_B;
private:
static void func2()
{
cout << "static void func2的调用" << endl;
}
};
int Person::m_A = 0;
void test01()
{
// 1.通过对象访问
Person p;
p.func();
cout << endl;
// 2.通过类名进行访问
Person::func();
//Person::func2(); //类外不能访问私有静态成员函数
}
int main()
{
test01();
return 0;
}
在c++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
#include
using namespace std;
#include
class Person1
{
};
class Person2
{
int m_A;
};
class Person3
{
int m_A;
static int m_B;
};
class Person4
{
int m_A;
static int m_B;
void func() {}
};
class Person5
{
int m_A; //非静态成员变量,属于类的对象上
static int m_B; //静态成员变量,不属于类的对象上
void func() {} //非静态成员函数,不属于类对象上
static void func2() {} //静态成员函数,不属于类对象上
};
void test01()
{
Person1 p1;
// 空对象占用内存空间为1
// 为区分空对象占内存的位置,c++给每个空对象分配一个字节空间。
cout << "size of p = " << sizeof(p1) << endl;
}
void test02()
{
// 占用内存空间为4
Person2 p2;
cout << "size of p = " << sizeof(p2) << endl;
}
void test03()
{
// 占用内存空间为4
Person3 p3;
cout << "size of p = " << sizeof(p3) << endl;
}
void test04()
{
// 占用内存空间为4
Person4 p4;
cout << "size of p = " << sizeof(p4) << endl;
}
void test05()
{
// 占用内存空间为4
Person4 p5;
cout << "size of p = " << sizeof(p5) << endl;
}
int main()
{
test01();
test02();
test03();
test04();
test05();
return 0;
}
通过4.3.1我们知道c++成员变量和成员函数是分开储存的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。
this指针指向被调用的成员函数所属的对象。
this指针是隐含每一个非静态成员函数的一种指针。
this指针不需要定义,直接使用即可。
this指针的用途:
#include
using namespace std;
class Person
{
public:
Person(int age)
{
cout << "Person的有参构造函数调用" << endl;
//m_Age = age;
this->age = age; //this指针指向被调用的成员函数 所属的对象
}
void PersonAddAge(Person &p)
{
this->age += p.age;
}
Person& PersonAddAge2(Person &p)
{
this->age += p.age;
return *this; // this指向p2的指针,*this指向的就是对象本体
}
Person PersonAddAge3(Person &p) //值方式返回,每次都拷贝构造新对象,不是返回原对象
{
this->age += p.age;
return *this; // this指向p2的指针,*this指向的就是对象本体
}
//int m_Age;
int age;
};
void test01()
{
Person p1(18); // p1调了有参构造,this指向p1。
cout << "p1的年龄为: " << p1.age << endl;
}
void test02()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1);
cout << "p2的年龄为: " << p2.age << endl;
cout << endl;
Person p3(10);
p3.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1); //链式编程思想
cout << "p3的年龄为: " << p3.age << endl;
cout << endl;
Person p4(10);
p4.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1); //每次拷贝出一个新对象,不是引用原对象
cout << "p4的年龄为: " << p4.age << endl;
cout << endl;
}
int main()
{
test01();
cout << endl;
test02();
return 0;
}
![[Pasted image 20240110101158.png]]
c++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性。
#include
using namespace std;
class Person
{
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()
{
Person *p= NULL;
p->showClassName();
p->showPersonAge(); //不判空,就会报错。报错原因是传入空指针,无法访问属性。
}
int main()
{
test01();
return 0;
}
常函数:
常对象:
#include
using namespace std;
class Person
{
public:
void showPerson() const //在成员函数后加const,修饰的是this指向,让指针指向的值也不可修改
{
//m_A = 100; // 报错,表达式不能修改
//this->m_A = 100; // 报错,被const修饰了,不能修改this指针指向的值
this->m_B = 100; //被mutable修改的变量,即使被const修改,this指针指向的值也可修改
}
void showPerson2()
{
//this = NULL; //this指针的本质是指针常量,指针的指向是不可更改的
this->m_A = 100; // this指针可以修改指针指向的值
}
int m_A;
mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值
};
void test01()
{
Person p;
p.showPerson();
p.showPerson2();
}
void test02()
{
const Person p1; //在对象前面加const,变为常对象
//p1.m_A = 100; //p1是常对象,不能修改普通成员属性
p1.m_B = 100; //m_B是特殊值,在常对象下也可以修改
p1.showPerson(); //常对象调用了常函数
//p1.showPerson2(); //常对象只能调用常函数
}
int main()
{
test01();
cout << endl;
test02();
return 0;
}
友元的目的就是让一个函数或者类访问另一个类中私有成员,友元的关键字为friend。
友元的三种实现
示例代码:
#include
using namespace std;
#include
class Building
{
//声明 友元可以访问类中私有成员
friend void goodGay(Building *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;
}
void test01()
{
Building building;
goodGay(&building);
}
int main()
{
test01();
return 0;
}
示例代码:
#include
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit();
Building *building;
};
class Building
{
friend class GoodGay;
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
building = new Building; //new什么数据类型就返回什么数据类型的指针
}
void GoodGay::visit()
{
cout << "好基友类正在访问: " << building->m_SittingRoom << endl;
cout << "好基友类正在访问: " << building->m_BedRoom << endl;
}
void test01()
{
//首先创建GoodGay对象,调用其构造函数,其中会new一个Building的对象,调用Building的构造函数,此时Building的构造中给该类的两个属性都附了一个初值,此时GoodGay对象就可以调用visit属性,访问Building中的属性。
GoodGay gg;
gg.visit();
}
int main()
{
test01();
return 0;
}
注:
class Building;
还报错error: Invalid use of incomplete type
,参考StackOverflow回答.示例代码:
#include
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit(); // 让visit函数可以访问Building中私有成员
void visit2(); // 让visit2函数不可以访问Building中私有成员
Building *building;
};
class Building
{
friend void GoodGay::visit(); //声明 编译器GoodGay类下的visit函数 是友元
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
building = new Building; //new什么数据类型就返回什么数据类型的指针
}
void GoodGay::visit()
{
cout << "visit 函数正在访问: " << building->m_SittingRoom << endl;
cout << "visit 函数正在访问: " << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
cout << "visit2 函数正在访问: " << building->m_SittingRoom << endl;
//cout << "visit2 函数正在访问: " << building->m_BedRoom << endl;
}
void test01()
{
//首先创建GoodGay对象,调用其构造函数,其中会new一个Building的对象,调用Building的构造函数,此时Building的构造中给该类的两个属性都附了一个初值,此时GoodGay对象就可以调用visit属性,访问Building中的属性。
GoodGay gg;
gg.visit();
gg.visit2();
}
int main()
{
test01();
return 0;
}
继承是面向对象三大特性之一
优点:减少重复代码
示例:
#include
using namespace std;
#include
class BasePage
{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java、Python、C++...(公共分类列表)" << endl;
}
};
class Java: public BasePage
{
public:
void content()
{
cout << "Java学科视频" << endl;
}
};
class Python: public BasePage
{
public:
void content()
{
cout << "Python学科视频" << endl;
}
};
class Cpp: public BasePage
{
public:
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
cout << "Java下载视频页面如下:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << endl;
cout << "Python下载视频页面如下:" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << endl;
cout << "C++下载视频页面如下:" << endl;
Cpp cc;
cc.header();
cc.footer();
cc.left();
cc.content();
}
int main()
{
test01();
return 0;
}
class A: public B;
注:派生类中的成员,包含两大部分。一类是从基类继承过来的,一类是自己增加的成员。从基类继承过来的表现其共性,而新增的成员体现了其个性。
继承方式有三种:
#include
using namespace std;
#include
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1: public Base1
{
public:
void func()
{
m_A = 10; //公共继承,公共权限
m_B = 10; //公共继承,保护权限
//m_C = 10; //父类私有,不可访问
}
};
class Son2: protected Base1
{
public:
void func()
{
m_A = 1; // 保护继承,变为保护成员
m_B = 1; // 保护继承,变为保护成员
//m_C = 10; // 父类私有,不可访问
}
};
class Son3: private Base1
{
public:
void func()
{
m_A = 100; // 私有继承,变为私有成员
m_B = 100; // 私有继承,变为私有成员
//m_C = 10; // 父类私有,不可访问
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;
// s1.m_B = 100; //公共继承,protected权限类外不能访问
}
void test02()
{
Son2 s2;
// s2.m_A = 100; // 保护继承,protected权限类外不能访问
// s2.m_B = 100; //保护继承,protected权限类外不能访问
}
void test03()
{
Son3 s3;
// s3.m_A = 100; // 私有继承,private权限类外不能访问
// s3.m_B = 100; //私有继承,private权限类外不能访问
}
int main()
{
test01();
test02();
test03();
return 0;
}
问题:从父类继承过来的成员,哪些属于子类对象中
示例:
#include
using namespace std;
#include
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son: public Base
{
public:
int m_D;
};
void test01()
{
// 父类所有非静态成员属性都会被子类继承下去,但是编译器隐藏了,所以访问不到
// 但是确实被继承下去了,所以内存占用为16
cout << "size of son = " << sizeof(Son) << endl;
}
示例代码:
#include
using namespace std;
#include
class Base
{
public:
Base()
{
cout << "Base构造函数!" << endl;
}
~Base()
{
cout << "Base析构函数!" << endl;
}
};
class Son: public Base
{
public:
Son()
{
cout << "Son构造函数!" << endl;
}
~Son()
{
cout << "Son析构函数!" << endl;
}
};
void test01()
{
Base b;
cout << endl;
Son s;
}
int main()
{
test01();
return 0;
}
![[Pasted image 20240103103600.png]]
继承中的构造和析构顺序如下:
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
示例代码:
#include
using namespace std;
#include
class Base
{
public:
Base()
{
m_A = 100;
}
void func()
{
cout << "base中的func调用" << endl;
}
int m_A;
};
class Son: public Base
{
public:
Son()
{
m_A = 200;
}
void func(int a)
{
cout << "Son中的func调用" << endl;
}
int m_A;
};
void test01()
{
Son s;
cout << "Son: m_A = " << s.m_A << endl;
cout << "Base: m_A = " << s.Base::m_A << endl; // 调用父类同名,加作用域
cout << endl;
s.func();
s.Base::func(100); // 调用父类同名,加作用域
}
int main()
{
test01();
return 0;
}
总结:如果子类中出现和父类同名的成员函数,子类同名成员会隐藏掉父类所有同名成员函数,如果想访问到父类中被隐藏的同名成员函数,需要加作用域。
问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致
#include
using namespace std;
#include
class Base
{
public:
Base()
{
m_A = 100;
}
static void func()
{
cout << "base中的static func()调用" << endl;
}
static void func(int a)
{
cout << "base中的static func(int a)调用" << endl;
}
static int m_A;
};
int Base::m_A = 100;
class Son:public Base
{
public:
static int m_A;
static void func()
{
cout << "Son中的static func()调用" << endl;
}
};
int Son::m_A = 200;
void test01()
{
Son s;
//通过对象访问
cout << "Son中m_A=" << s.m_A << endl;
cout << "Base中m_A=" << s.Base::m_A << endl;
cout << endl;
//通过类名访问
cout << "Son中m_A=" << Son::m_A << endl;
cout << "Base中m_A=" << Base::m_A << endl;
//第一个::代表通过类名方式访问,第二个::代表访问父类作用域下
cout << "Base中m_A=" << Son::Base::m_A << endl;
}
void test02()
{
//通过对象访问
Son s;
s.func();
s.Base::func();
cout << endl;
//通过类名访问
Son::func();
Son::Base::func();
Son::Base::func(100);
}
int main()
{
test01();
cout << "*******************" << endl;
test02();
return 0;
}
多态是c++面向对象三大特性之一,分为两类:
静态多态和动态多态区别:
示例:
#include
using namespace std;
#include
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 &animal = cat;
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
int main()
{
test01();
return 0;
}
![[Pasted image 20240110104042.png]]
注:
动态多态满足条件:
动态多态使用:
示例:
#include
using namespace std;
#include
class Animal
{
public:
void speak()
{
cout << "动物在说话" << endl;
}
};
void test02()
{
//void speak()占1个字节
//改为virtual void speak()占8个字节,我的电脑显示8个,视频讲的是4个字节
cout << "size of Animal = " << sizeof(Animal) << endl;
}
int main()
{
test02();
return 0;
}
Animal内部结构:
![[Pasted image 20240103115600.png]]
Catl类内部结构:
![[Pasted image 20240103120039.png]]
Animal &animal = cat;
animal.speak();
注:可以用Vusial studio的命令提示符查看类的结构,验证一下。
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
普通写法:
#include
using namespace std;
#include
class Calculator
{
public:
int getResult(string oper)
{
if(oper == "+")
{
return m_Num1 + m_Num2;
}
else if(oper == "-")
{
return m_Num1 - m_Num2;
}
else if(oper == "*")
{
return m_Num1 * m_Num2;
}
}
int m_Num1;
int m_Num2;
};
void test01()
{
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
}
int main()
{
test01();
return 0;
}
多态写法:
#include
using namespace std;
#include
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
class Add: public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 + m_Num2;
}
};
class Sub: public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 - m_Num2;
}
};
class Mul: public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 * m_Num2;
}
};
void test01()
{
//多态使用条件:父类指针或者引用,指向子类对象
AbstractCalculator *abc = new Add; //父类指针,指向new的对象
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
delete abc; //用完记得销毁
abc = new Sub;
abc->m_Num1 = 100;
abc->m_Num2 = 100;
cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
delete abc; //用完记得销毁
abc = new Mul;
abc->m_Num1 = 100;
abc->m_Num2 = 100;
cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
delete abc; //用完记得销毁
}
int main()
{
test01();
return 0;
}
开闭原则:对扩展进行开放,对修改进行关闭。
多态的优点:
总结:C++开发提倡利用多态设计程序架构,因为多态优点很多。
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数。
语法:virtual 返回值类型 函数名(参数列表)= 0;
当类中有了纯虚函数,这个类也称为抽象类。
抽象类特点:
示例:
#include
using namespace std;
#include
class Base
{
public:
//纯虚函数
virtual int func() = 0;
int m_Num1;
int m_Num2;
};
class Son1: public Base
{
public:
};
class Son2: public Base
{
public:
virtual int func()
{
cout << "func函数调用" << endl;
}
};
void test01()
{
//栈区和堆区都无法实例化对象
//Base b;
//new Base;
//Son1 s1; //子类没有重写,也是抽象类,无法实例化对象
Base *base = new Son2;
base->func();
}
int main()
{
test01();
return 0;
}
饮品制作流程:煮水-冲泡-倒入杯中-加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶
#include
using namespace std;
#include
class AbstractDrinking
{
public:
//纯虚函数
virtual int Boil() = 0;
virtual int Brew() = 0;
virtual int PourInCup() = 0;
virtual int AddSomething() = 0;
void makeDrink()
{
Boil();
Brew();
PourInCup();
AddSomething();
}
};
class Coffee: public AbstractDrinking
{
public:
virtual int Boil()
{
cout << "烧水" << endl;
}
virtual int Brew()
{
cout << "泡咖啡" << endl;
}
virtual int PourInCup()
{
cout << "倒入杯中" << endl;
}
virtual int AddSomething()
{
cout << "加牛奶和糖" << endl;
}
};
class Tea: public AbstractDrinking
{
public:
virtual int Boil()
{
cout << "烧水" << endl;
}
virtual int Brew()
{
cout << "泡茶" << endl;
}
virtual int PourInCup()
{
cout << "倒入杯中" << endl;
}
virtual int AddSomething()
{
cout << "加入柠檬" << endl;
}
};
void doWork(AbstractDrinking *abc)
{
abc->makeDrink();
delete abc; //内存释放
}
void test01()
{
doWork(new Coffee);
cout << endl;
doWork(new Tea);
}
int main()
{
test01();
return 0;
}
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。解决方式:将父类中的析构函数改为虚析构或者纯虚析构。
语法:
virtual ~类名(){}
virtual ~类名() = 0;
虚析构和纯虚析构共性:
虚析构和纯虚析构区别:
虚析构示例:
#include
using namespace std;
#include
class Animal
{
public:
Animal()
{
cout << "Animal构造调用" << endl;
}
//虚析构
virtual ~Animal()
{
cout << "Animal析构调用" << endl;
}
//纯虚函数
virtual void speak() = 0;
};
class Cat: public Animal
{
public:
Cat(string name)
{
cout << "Cat构造调用" << endl;
m_Name = new string(name);
}
virtual void speak()
{
cout << *m_Name <<"小猫在说话" << endl;
}
~Cat()
{
if(m_Name != NULL)
{
cout << "Cat析构调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string *m_Name;
};
void test01()
{
Animal *animal = new Cat("Tom");
animal->speak();
//父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
//改为虚析构即可解决这个问题
delete animal;
}
int main()
{
test01();
return 0;
}
纯虚析构示例:
#include
using namespace std;
#include
class Animal
{
public:
Animal()
{
cout << "Animal构造调用" << endl;
}
//纯虚析构
// 纯虚析构需要声明实现,有纯虚析构函数的类也是抽象类,无法实例化对象
virtual ~Animal() = 0;
//纯虚函数
virtual void speak() = 0;
};
Animal::~Animal()
{
cout << "Animal纯虚析构调用" << endl;
}
class Cat: public Animal
{
public:
Cat(string name)
{
cout << "Cat构造调用" << endl;
m_Name = new string(name);
}
virtual void speak()
{
cout << *m_Name <<"小猫在说话" << endl;
}
~Cat()
{
if(m_Name != NULL)
{
cout << "Cat析构调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string *m_Name;
};
void test01()
{
Animal *animal = new Cat("Tom");
animal->speak();
//父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
//改为虚析构即可解决这个问题
delete animal;
}
int main()
{
test01();
return 0;
}
总结:
电脑主要组成部件为CPU、显卡、内存条。将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商。创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口。测试时组装三台不同的电脑进行工作。
![[Pasted image 20240103152417.png]]
#include
using namespace std;
#include
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;
VideoCard *m_vc;
Memory *m_mem;
};
class IntelCPU: public CPU
{
public:
virtual void calculate()
{
cout << "intel的cpu开始计算" << endl;
}
};
class IntelVideoCard: public VideoCard
{
public:
virtual void display()
{
cout << "intel的显卡开始计算" << endl;
}
};
class IntelMemory: public Memory
{
public:
virtual void storage()
{
cout << "intel的内存条开始计算" << endl;
}
};
class LenovoCPU: public CPU
{
public:
virtual void calculate()
{
cout << "Lenovo的cpu开始计算" << endl;
}
};
class LenovoVideoCard: public VideoCard
{
public:
virtual void display()
{
cout << "Lenovo的显卡开始计算" << endl;
}
};
class LenovoMemory: public Memory
{
public:
virtual void storage()
{
cout << "Lenovo的内存条开始计算" << endl;
}
};
void test01()
{
//第一台电脑零件
CPU *intelCpu = new IntelCPU;
VideoCard *intelCard = new IntelVideoCard;
Memory *intelMem = new IntelMemory;
//创建第一台电脑
cout << "第一台电脑开始工作:" << endl;
Computer *computer1 = new Computer(intelCpu, intelCard, intelMem);
computer1->work();
delete computer1;
cout << endl;
//第二台电脑组装
cout << "第二台电脑开始工作:" << endl;
Computer *computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);
computer2->work();
delete computer2;
cout << endl;
cout << "第三台电脑开始工作:" << endl;
Computer *computer3 = new Computer(new IntelCPU, new IntelVideoCard, new LenovoMemory);
computer3->work();
delete computer3;
}
int main()
{
test01();
return 0;
}