目录
1.1 继承
1.1.1 为什么需要继承
1.1.2 继承基本语法
1.1.3 继承方式
1.1.3 C++的初始化列表
1.1.4 继承中的构造与析构函数
1.1.5 继承中同名非静态成员处理
1.1.6 继承中同名静态成员处理
1.1.7 多继承
1.2 多态
1.2.1 多态基本概念
1.2.2 静态联编和动态联编
1.2.3 虚函数原理剖析
1.2.4 多态实现--计算器
1.2.5 纯虚函数
1.2.6 虚析构与纯虚析构
1.2.7 向上向下的类型转换
1.2.8 重写、重载、重定义
1.2.8 练习:电脑安装
1.3 封装
生活中有许多交通工具:车、畜牲、飞机等;就拿车来说,每辆车都有相同的属性:比如车牌号、生产厂商是车都有的。但每辆车又有不同的属性,比如有的车是自动档就没有换档的零部件、有些车加汽油就没有充电插槽。
要设计每个型号的汽车类,既有所有汽车共有的属性,也有自己特有的属性。那是否可以把所有属性全写进一个类,可以但是没必要,本节将介绍如何解决这个问题。
class CarBase { public: string carName; int carNumber; }; class Tesla : public CarBase { public: string chargeTools; }; void test01() { Tesla tesla; tesla.carName = "特斯拉" ; tesla.carNumber = 66666; tesla.chargeTools = "electric"; cout << "carName is : " << tesla.carName << endl << "car number is : " << tesla.carNumber << endl << "chaege ways is : " << tesla.chargeTools << endl; }
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 carName is : 特斯拉 car number is : 66666 chaege ways is : electric 进程已结束,退出代码为 0
1.继承语法:class + 子类(派生类) + : + 继承方式 + 父类(基类)
2.优势:提高代码复用性
1.继承方式简介
下面我们根据代码验证一下。
2.公共继承
class CarBase { public: string carName; int carNumber; protected: int brockentimes; private: int bornDate; }; class Tesla : public CarBase { public: void ChangeInfo() { this -> carName = "特斯拉" ; //父类公共权限子类变成公共权限 this -> carNumber = 6666 ; //父类公共权限子类变成公共权限 this -> brockentimes = 3; //父类保护权限子类变成保护权限 //this -> bornDate = 990311 ; 父类私有成员子类不可访问,因为被继承下来了但是不可访问 } }; void test01() { Tesla tesla; tesla.carName = "捷达" ; //在tesla中,carame是公共权限,类外可以访问 tesla.carNumber = 66665 ; //在tesla中,carnumber是公共权限,类外可以访问。 //tesla.brockentimes = 6; //在tesla中,brockentimes是保护权限,类外不可以访问。 }
父类公共权限子类变成公共权限
父类保护权限子类变成保护权限
父类私有成员子类不可访问,因为被继承下来了但是不可访问
在子类对象中,carame是公共权限,类外可以访问
在子类对象中,brockentimes是保护权限,类外不可以访问
3. 保护继承
class BWM : protected CarBase { public: void ChangeInfo() { this -> carName = "特斯拉" ; //父类公共权限子类变成保护权限 this -> carNumber = 6666 ; //父类公共权限子类变成保护权限 this -> brockentimes = 3; //父类保护权限子类变成保护权限 //this -> bornDate = 990311 ; 父类私有成员子类不可访问,因为被继承下来了但是不可访问 }; void test02() { BWM bwm; //tesla.carName = "捷达" ; //在BWM中,carame是保护权限,类外不可以访问 //tesla.carNumber = 66665 ; //在BWM中,carnumber是保护权限,类外不可以访问。 //tesla.brockentimes = 6; //在BWM中,brockentimes是保护权限,类外不可以访问。 }
父类公共权限子类变成保护权限
父类保护权限子类变成保护权限
父类私有成员子类不可访问,因为被继承下来了但是不可访问
在子类对象中,carame是保护权限,类外不可以访问
在子类对象中,carnumber是保护权限,类外不可以访问
在子类对象中,brockentimes是保护权限,类外不可以访问
4.私有继承
class bicycle : private CarBase { public: void ChangeInfo() { this -> carName = "特斯拉" ; //父类公共权限子类变成私有权限 this -> carNumber = 6666 ; //父类公共权限子类变成私有权限 this -> brockentimes = 3; //父类保护权限子类变成私有权限 //this -> bornDate = 990311 ; 父类私有成员子类不可访问,因为被继承下来了但是不可访问 }; void test03() { bicycle bicycle1; //bicycle1.carName = "捷达" ; //在bicycle中,carame是私有权限,类外不可以访问 //bicycle1.carNumber = 66665 ; //在bicycle中,carnumber是私有权限,类外不可以访问。 //bicycle1.brockentimes = 6; //在bicycle中,brockentimes是私有权限,类外不可以访问。 }
父类公共权限子类变成私有权限
父类保护权限子类变成私有权限
父类私有成员子类不可访问,因为被继承下来了但是不可访问
在子类对象中,carame是私有权限,类外不可以访问
在子类对象中,carnumber是私有权限,类外不可以访问
在子类对象中,brockentimes是私有权限,类外不可以访问
5.总结:一个博主的总结,觉得总结的不错
在成员访问模式中:
public 表示共有;类的数据成员和函数可以被该类对象和派生类访问。
private 私有型;自己的类可以访问,但派生类不能访问。
protected 保护型;自身类和派生类可以访问相当于自身的private型成员,它同private的区别就是在对待派生类的区别上。C++中 public,protected, private 访问标号小结
第一:private, public, protected 访问标号的访问范围。private:只能由1.该类中的函数、2.其友元函数访问。不能被任何其他访问,该类的对象也不能访问。
protected:可以被1.该类中的函数、2.子类的函数、以及3.其友元函数访问。但不能被该类的对象访问。
public:可以被1.该类中的函数、2.子类的函数、3.其友元函数访问,也可以由4.该类的对象访问。
注:友元函数包括3种:设为友元的普通的非成员函数;设为友元的其他类的成员函数;设为友元类中的所有成员函数。第二:类的继承后方法属性变化。
private 属性不能够被继承。
使用private继承,父类的protected和public属性在子类中变为private;
使用protected继承,父类的protected和public属性在子类中变为protected;
使用public继承,父类中的protected和public属性不发生改变;如下所示:
public: protected: private:
public继承 public protected 不可用
protected继承 protected protected 不可用
private继承 private private 不可用
protected继承和private继承能降低访问权限。
————————————————
版权声明:本文为CSDN博主「浮鱼浮鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_50849959/article/details/116595077
我们将解释观察下面的代码:
class InitList { public: InitList():m_a(10),m_b(20),m_c(30){} public: int m_a; int m_b; int m_c; }; void test06() { InitList a; cout << "a.m_a = " << a.m_a << endl << "a.m_b = " << a.m_b << endl << "a.m_c = " << a.m_c << endl ; }
初始化列表是将第一个属性初始化为.....,第二个属性初始化为.....,第三个属性初始化为.....,但这样不是将属性值写死了吗?我们正常这么写:
class InitList { public: InitList(int a,int b,int c):m_a(a),m_b(b),m_c(c){} public: int m_a; int m_b; int m_c; }; void test06() { InitList a(70,80,90); cout << "a.m_a = " << a.m_a << endl << "a.m_b = " << a.m_b << endl << "a.m_c = " << a.m_c << endl ; }
我们看这么一段代码
class InitList { public: InitList(int a,int b,int c):m_a(a),m_b(b),m_c(c){} public: int m_a; int m_b; int m_c; }; void test06() { InitList a(70,80,90); cout << "a.m_a = " << a.m_a << endl << "a.m_b = " << a.m_b << endl << "a.m_c = " << a.m_c << endl ; }
我们理清它的逻辑,我们在test06中通过调用类InitList的有参构造函数来对其初始化,然后70,80,90这三个值传到 int a,int b,int c三个形参中,在传递给右面的初始化列表,将m_a属性赋值70......
1.结论
只介绍结论
子类也会调用父类构造函数,创建子类时先调用父类的构造,再调用自身的构造,析构函数相反。
另外,还记得如果一个类中有其他类的成员,是先构造其他类。
但如果在子类有其他类,是先调用父类还是其他类呢?先走父类构造,在走子类成员构造,再调用自身构造。
2.一些容易犯错的点
我们看下面一段代码:
class Base1 { public: Base1(int a) { cout << "Base1 struct function is running !" << endl; } }; class son1 : public Base1 { public: son1() { cout << "son1 struct function is running !" << endl; } };
这段代码会报错:
为什么呢?
父类函数有有参构造函数,则父类就没有默认构造函数了。那么根据结论,调用子类构造函数前要调用父类的默认构造,此时父类函数没有默认构造,编译器报错。
解决1:提供默认构造函数
class Base1 { public: Base1() { } Base1(int a) { cout << "Base1 struct function is running !" << endl; } }; class son1 : public Base1 { public: son1() { cout << "son1 struct function is running !" << endl; } };
解决2:利用初始化列表
class Base1 { public: Base1(int a) { this->m_a = a; cout << "Base1 struct function is running !" << endl; } int m_a; }; class son1 : public Base1 { public: son1() : Base1(1000) { cout << "son1 struct function is running !" << endl; } }; void test05() { //son1 s(100); son1 s2; //cout << "s::m_a = :" << s.m_a << endl; cout << "s2::m_a = :" << s2.m_a << endl; }
我们看这段代码,我们利用了初始化列表,显示的调用父类的其他(有参)构造函数,我们用这种方法,使得执行son1 s2时,执行Base1的构造函数传值10,即调用了父类的有参构造函数,使得m_a获取了10这个值。
class Base1 { public: Base1(int a) { this->m_a = a; cout << "Base1 struct function is running !" << endl; } int m_a; }; class son1 : public Base1 { public: son1(int a = 1000) : Base1(a) { cout << "son1 struct function is running !" << endl; } }; void test05() { son1 s(100); son1 s2; cout << "s::m_a = :" << s.m_a << endl; cout << "s2::m_a = :" << s.m_a << endl; } int main() { test05(); return 0; }
我们这么改,增加了代码的通用性,我们在执行到s(100)时,将参数传给自身的构造函数,构造函数丢掉默认值,选择100传给继承来的Base1属性,Base1属性初始化m_a的值。
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 Base1 struct function is running ! son1 struct function is running ! Base1 struct function is running ! son1 struct function is running ! s::m_a = :100 s2::m_a = :1000
观察结果!我们发现其实是先调用子类的构造函数,不过没执行完就把控制权交给父类了。
猜猜下段代码会发生什么
class Father { public: Father() { m_A = 10; } int m_A; }; class Child : public Father { public: Child() { m_A = 100; } int m_A; }; void test07() { Child child; cout << "clild's m_A = " << child.m_A << endl; }
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 clild's m_A = 100
说明这是就近原则。优先使用自己的。但如何找父类的同名属性呢?
利用作用域。
class Father { public: Father() { m_A = 10; } int m_A; }; class Child : public Father { public: Child() { m_A = 100; } int m_A; }; void test07() { Child child; cout << "clild's m_A = " << child.m_A << endl; cout << "Father's m_A is : " << child.Father::m_A << endl; }
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 clild's m_A = 100 Father's m_A is : 10
函数也是如此。
但如果父类发生了重载呢?我们看下面一段代码?
假设我们现在的类是这样的
class Father { public: Father() { m_A = 10; } void func() { cout << "Father's funcc" << endl; } void func(int a) { cout << "Father's funcc(int a)" << endl; } int m_A; }; class Child : public Father { public: Child() { m_A = 100; } void func() { cout << "child's funcc" << endl; } int m_A; };
void test07() { Child child; child.func(); }
我们这么调用代码,根据上文描述,是调用的子类的func函数。事实也如此。
但我们如果这么调用,则编译器会报错。
child.func(3);
他不会调用父类对func的重载。事实上,当子类重新定义了父类中的同名成员函数,子类的成员函数会隐藏掉父类中所有重载版本的同名成员,但可以通过作用域来访问。
void test07() { Child child; child.Father::func(3); }
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 Father's funcc(int a) 进程已结束,退出代码为 0
class Father { public: static int m_A; }; int Father::m_A = 20; class Child : public Father { public: void func() { cout << "child's funcc" << endl; } static int m_A; }; int Child::m_A = 10; void test07() { Child child; cout << "m_a = " << child.m_A << endl ; cout << "Father's m_a = " << child.Father::m_A << endl; }
我们运行上面一段代码:
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 m_a = 10 Father's m_a = 20
好像和非静态的没有什么区别。
因为是静态变量也可直接用类名来访问,下面是代码以及执行结果。
cout << "Child's m_A is " << Child::m_A << endl; cout << "Father's m_A is " << Father::m_A << endl; cout << "Father's m_A is " << Child::Father::m_A << endl;
Child's m_A is 10 Father's m_A is 20 Father's m_A is 20
对于静态成员函数,我们要知道,静态成员函数是无法访问类中的变量的。
也可以通过类名和对象访问。结论和非静态成员函数一样。
1.用途
我们可以从多个类去继承。
2.语法
class ChildClass : public Father1,public Father2
3.菱形继承
I .为什么需要菱形继承:
两个派生类继承同一个基类而某个类又同时继承两个派生类时,这时发生了菱形继承。
比如
而年龄只要一份就够了,要两份没什么用呀!
羊继承了动物的数据和函数,驼也继承了动物的数据和函数,当草泥马使用两者的函数或者数据时,会产生二义性。
草泥马继承动物的数据两份,事实上我们需要一份就够了。
Ⅱ.建立模型
class Animal { public: int age; }; class Sheep : public Animal { }; class tuo : public Animal { }; class caonima : public Sheep,public tuo { };
Ⅲ.修改方法一:制定作用域但存在奇义
我们可以发现,直接赋值会报错:编译器并不知道要赋值给谁
Non-static member 'age' found in multiple base-class subobjects of type 'Animal': class caonima -> class Sheep -> class Animal class caonima -> class tuo -> class Animal member found by ambiguous name lookup
void test08() { caonima cao; cao.Sheep::age = 10 ; cao.tuo::age = 15; cout << "Sheep's age is : " << cao.Sheep::age << endl ; cout << "tuo's age is : " << cao.tuo::age << endl ; }
我们这么修改代码可以给sheep和tuo的age属性赋值,但现在cao的age是多少,其实不管访问与否都没有什么意义。
Ⅳ.修改方法二:虚继承
class Animal { public: int age; }; class Sheep : virtual public Animal { }; class tuo : virtual public Animal { }; class caonima : public Sheep,public tuo { }; void test08() { caonima cao; cao.Sheep::age = 10 ; cao.tuo::age = 15; cout << "Sheep's age is : " << cao.Sheep::age << endl ; cout << "tuo's age is : " << cao.tuo::age << endl ; }
先说一个概念:
虚继承:在继承的前面写个关键字virtual,这时animal类叫做虚基类
我们再来执行这个程序:
/home/lhw/桌面/C++/day6/cmake-build-debug/day6 Sheep's age is : 15 tuo's age is : 15
现在说明在内存中只有一份age了,我们可以这样访问age。
void test08() { caonima cao; cao.age = 10 ; cout << "age is : " << cao.age << endl; }
Ⅴ.虚继承的内部实现
当发生虚继承时,sheep和tuo继承的是父类的vbptr(虚基类)指针,子类对于该属性不分配内存空间,仅仅有指向父类该属性的指针,这时看这两行代码
cao.Sheep::age = 10 ;
cao.tuo::age = 15;也就理解了为什么最终数据是15吧。
多态性提供接口与具体实现的一种隔离,改善了代码的可读性和组织性,同时又让生成的程序有一定量的扩展性。项目不仅在创建初期可以拓展,在需要有新功能添加时也能拓展。
函数重载和运算符重载是一种静态多态。前面已经介绍,不再阐述。
Ⅰ.对于有父子关系的两个类指针或者引用是可以相互转换的
class Animal { public: void speak() { cout << "animals are speaking!" << endl; } }; class Cat : public Animal { public: void speak() { cout << "Cat is speaking!" << endl; } }; void Speak(Animal & animal) { animal.speak(); } void test01() { Cat cat; Speak(cat); }
我们向父类传进一个子类对象,编译器并没有报错,我们运行以下看看结果。
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 animals are speaking!
这是为什么呢?
我们在dospeak函数中定义了animal的引用,因此函数执行地址早就绑定到了animal那个类的初始地址了。因此不管我们调用猫还是狗或者其他动物,都会输出animal下的speak方法。
地址早就绑定好了,成为静态联编。
Ⅱ.那如果想调用小猫在说话呢?是不是地址就不能早就绑定好了勒!
我们选择在运行时绑定,成为动态联编
我们在父类中的speak前面加上virtual关键字,构成虚函数。这时候再来运行
class Animal { public: virtual void speak() { cout << "animals are speaking!" << endl; } }; class Cat : public Animal { public: void speak() { cout << "Cat is speaking!" << endl; } }; void Speak(Animal & animal) { animal.speak(); } void test01() { Cat cat; Speak(cat); }
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 Cat is speaking!
我们看看编译器内部发生了什么。
这时候调用地址是在运行时根据传入对象(Cat类对象)的不同而去绑定。
Ⅲ.动态多态的产生条件
首先要有继承关系
父类中有虚函数
子类中重写了虚函数
父类的指针或引用指向子类对象
我们知道,正常创建一个类内函数是不会占用类的空间的。但我们将其加上Virtual关键字,类内就会多出来一个指针vfptr(虚函数指针)指向虚函数表(vftable),虚函数表记录虚函数的函数入口首地址(&Animal:speak),如果创建了子类继承了父类,那么子类也会创建一个vfptr和一个vftable,vftable也会有animal成员函数speak的首地址(&Animal:speak)。如果我们发生了重写(子类重写父类中的虚函数:返回值参数名形参列表必须相同),会覆盖到自己的虚函数表中,即把父类的speak函数(&Animal:speak)覆盖,只有自己的(&Cat:speak),但不改变父类的虚函数表。
我们执行这段代码
Animal * animal = new Cat;
父类指针指向子类对象,这是一个cat对象,切记!
animal -> speak();
这时候会从子类cat中寻找speak的入口。
class CalculatorBase { public: virtual int getResult() { } int m_A; int m_B; }; class AddCalculator : public CalculatorBase { public: virtual int getResult() { return m_A + m_B; } }; class SubCalculator : public CalculatorBase { public: virtual int getResult() { return m_A - m_B; } }; class MulCalculator : public CalculatorBase { public: virtual int getResult() { return m_A * m_B; } }; void test03() { CalculatorBase * calculatorBase = new AddCalculator; calculatorBase->m_A = 100; calculatorBase->m_B = 200; cout << "M_A + M_B = " << calculatorBase->getResult() << endl ; delete calculatorBase; calculatorBase = new SubCalculator; calculatorBase->m_A = 50 ; calculatorBase->m_B = 130; cout << "M_A - M_B = " << calculatorBase->getResult() << endl ; }
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 M_A + M_B = 300 M_A - M_B = -80
1.用处
Ⅰ.用处
如果父类中的实现没有任何意义,可以写成一个纯虚实现。
Ⅱ.语法
2.语法
virtual int getResult() = 0;
Ⅲ.注意
3.注意
1.如果一个类含有纯虚函数,那么这个类无法实例化对象了,这个类我们称之为抽象类。
class CalculatorBase { public: virtual int getResult() = 0; int m_A; int m_B; };
2.抽象类的子类必须重写父类中的纯虚函数,否则子类也属于抽象类。
1.一个案例
class Animal { public: Animal() { cout << "Animal's construct" << endl ; } virtual void speak() { cout << "animals are speaking!" << endl; } ~Animal() { cout << "Animal's end" << endl ; } }; class Cat : public Animal { public: Cat(char * name) { this -> m_Name = new char[strlen(name) +1]; strcpy(this->m_Name,name); cout << "cat's construct" << endl ; } void speak() { cout << "Cat " << this->m_Name <<"is speaking!" << endl; } ~Cat() { if(this->m_Name) { delete [] this->m_Name; this->m_Name = NULL; } cout << "cat's end" << endl ; } char * m_Name; }; void test01() { Animal * animal = new Cat("xiaohu"); animal->speak(); }
我们看这一段代码的执行结果:
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 Animal's construct cat's construct Cat xiaohu is speaking!
那么问题来了,为什么没有释放。
animal:new出来的东西创建在堆区,需要手动释放。修改test01函数即可:
void test01() { Animal * animal = new Cat("xiaohu"); animal->speak(); delete animal; }
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 Animal's construct cat's construct Cat xiaohuis speaking! Animal's end
那么为什么 cat的析构函数没有被调用呢?
这是因为不会运行子类的析构函数的原因,因为delete的是animal而不是cat。
解决:虚析构
如果子类中有指向堆区的属性(类似释放堆的代码),那么要利用虚析构技术,在delete的时候调用子类的析构函数。
完整的修改完的代码及运行结果如下:
class Animal { public: Animal() { cout << "Animal's construct" << endl ; } virtual void speak() { cout << "animals are speaking!" << endl; } virtual ~Animal() { cout << "Animal's end" << endl ; } }; class Cat : public Animal { public: Cat(char * name) { this -> m_Name = new char[strlen(name) +1]; strcpy(this->m_Name,name); cout << "cat's construct" << endl ; } void speak() { cout << "Cat " << this->m_Name <<"is speaking!" << endl; } ~Cat() { if(this->m_Name) { delete [] this->m_Name; this->m_Name = NULL; } cout << "cat's end" << endl ; } char * m_Name; }; void test01() { Animal * animal = new Cat("xiaohu"); animal->speak(); delete animal; }
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 Animal's construct cat's construct Cat xiaohuis speaking! cat's end Animal's end
纯虚析构与纯虚函数类似,但不同的是,纯虚析构其要有声明也要有实现,类内声明类外实现,代码如下。
class Animal { public: Animal() { cout << "Animal's construct" << endl ; } virtual void speak() { cout << "animals are speaking!" << endl; } virtual ~Animal() = 0; }; Animal::~Animal() { cout << "Animal's end" << endl ; } class Cat : public Animal { public: Cat(char * name) { this -> m_Name = new char[strlen(name) +1]; strcpy(this->m_Name,name); cout << "cat's construct" << endl ; } void speak() { cout << "Cat " << this->m_Name <<"is speaking!" << endl; } ~Cat() { if(this->m_Name) { delete [] this->m_Name; this->m_Name = NULL; } cout << "cat's end" << endl ; } char * m_Name; }; void test01() { Animal * animal = new Cat("xiaohu"); animal->speak(); delete animal; }
1.向下类型转换
我们直到,子类占用的空间一定比父类大。我们考虑这么一段代码。
Animal * animal = new Animal; Cat * cat = (Cat*)animal;
这段代码是不安全的,因为Animal寻址范围小,如果强转成cat*的话,使其寻址范围变大,可能访问到不该访问的东西。
2.向上类型转换
安全!
即子类指针转换成父指针可以,反之不可以。
且如果发生了多态,转换永远是安全的
Animal * animal = new Cat; Cat * cat = (Cat*)animal;
1.重载
同一个作用域
参数个数、参数顺序、参数类型不同
和函数返回值没有关系
const也可以作为重载条件 //do(const teacher & t) do(teacher & t)
2.重定义(覆盖)
有继承
子类重新定义父类的同名成员(非virtual函数)
3.重写(隐藏)
有继承
子类重写父类的virtual函数
函数返回值、函数名字、函数参数必须和基类中的虚函数一致
4.code
class A { public: //同一作用域下func1重载 void func1(){}; void func1(int a){}; void func1(int a,int b){}; void func2(){}; virtual void fun3(){}; }; class B : public A { public: //重定义基类的func2,隐藏了基类的func2方法 void func2(){}; //重写基类的fun3函数,也可以是覆盖基类的func3 virtual void func3(){}; };
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 * cpu1,Videocard * videocard1,Memory * memory) { this ->cpu = cpu1; this ->videocard = videocard1; this ->memory = memory; } void dowork() { this -> cpu -> calculate(); this ->memory ->storage(); this ->videocard ->display(); } CPU * cpu; Videocard * videocard; Memory * memory; ~Computer() { if(this->cpu!=NULL) { delete this->cpu; this->cpu = NULL; } if(this->videocard!=NULL) { delete this->videocard; this->videocard = NULL; } if(this->memory!=NULL) { delete this->memory; this->memory = NULL; } } }; class intelCPU : public CPU { public: void calculate() { cout << "INTEL CPU is calculating now" << endl ; } }; class intelVideoCard : public Videocard { public: void display() { cout << "INTEL Videocard is displaying now" << endl ; } }; class intelMemory : public Memory { public: void storage() { cout << "INTEL Memory is storage now" << endl ; } }; class levonoCPU : public CPU { public: void calculate() { cout << "levono CPU is calculating now" << endl ; } }; class levonoVideoCard : public Videocard { public: void display() { cout << "levono Videocard is displaying now" << endl ; } }; class levonoMemory : public Memory { public: void storage() { cout << "levono Memory is storage now" << endl ; } }; void test06() { cout << "第一台电脑组装" << endl ; CPU * intelcpu = new intelCPU; Videocard * lenovoVideocard = new levonoVideoCard; Memory * lenovomemory = new levonoMemory; Computer c1(intelcpu,lenovoVideocard,lenovomemory); c1.dowork(); }
/home/lhw/桌面/C++/day7/cmake-build-debug/day7 第一台电脑组装 INTEL CPU is calculating now levono Memory is storage now levono Videocard is displaying now
1.封装的思想
①:该隐藏的数据私有化,该公开的公有化(接口)(private、public);
②:目的就是为了分工合作,有助于使用的方便和安全性;
③:防止不必要的扩展。
2.代码解析
class A { public: void changea(int a) { this -> m_a = a; } int geta() { return m_a; } private: int m_a; }; void test() { A a; a.changea(5); cout << "a = " << a.geta() << endl ; }
封装的思想就是把变量私有化,通过对外界提供接口去访问或者赋值。