组合关系是总体与部分的关系。
组合关系的特色:
A、将其它类的对象做为当前类的成员使用
B、当前类的对象与成员对象的生命周期相同
C、成员对象在用法上与普通对象相同
Computer类由其它多个部件类组合而成,当Computer销毁时,其它部件对象同时销毁。ios
#include <iostream> using namespace std; class Memory { public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout << "~Memory()" << endl; } }; class Disk { public: Disk() { cout << "Disk()" << endl; } ~Disk() { cout << "~Disk()" << endl; } }; class CPU { public: CPU() { cout << "CPU()" << endl; } ~CPU() { cout << "~CPU()" << endl; } }; class MainBoard { public: MainBoard() { cout << "MainBoard()" << endl; } ~MainBoard() { cout << "~MainBoard()" << endl; } }; class Computer { Memory mMem; Disk mDisk; CPU mCPU; MainBoard mMainBoard; public: Computer() { cout << "Computer()" << endl; } void power() { cout << "power()" << endl; } void reset() { cout << "reset()" << endl; } ~Computer() { cout << "~Computer()" << endl; } }; int main(int argc, char *argv[]) { Computer c; c.reset(); return 0; }
继承关系是类之间的父子关系。继承关系的特色以下:
A、子类拥有父类的全部属性和行为
B、子类也是一种特殊的父类
C、子类对象能够当父类对象使用
D、子类中能够添加父类没有的属性和方法
E、子类对象能够直接初始化为父类对象
F、子类对象能够直接赋值给父类对象
G、继承是面向对象编程中代码复用的重要手段编程
#include <iostream> using namespace std; class Parent { public: Parent(int i = 0) { member = i; } void method() { cout << "member = " << member << endl; } private: int member; }; class Child : public Parent { public: Child(int i = 0, int j = 0):Parent(i) { childMember = j; } void childMethod() { method(); cout << "childMember = "<< childMember << endl; } private: int childMember; }; int main(int argc, char *argv[]) { Child child(1,2); child.method(); child.childMethod(); return 0; }
定义类时根据类的设计需求肯定成员的访问级别,规则以下:
A、public修饰的成员能够被外部访问。
B、protected修饰的成员不能够被外部访问,但能够被子类访问。
C、private修饰的成员不能够被外部和子类访问。ide
#include <iostream> using namespace std; class Parent { public: Parent(int a = 0, int b = 0, int c = 0) { pub = a; pro = b; pri = c; } void method() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; cout << "pri = " << pri << endl; } public: int pub; protected: int pro; private: int pri; }; class Child : public Parent { public: Child(int a = 0, int b = 0):Parent(a, b) { } void childMethod() { pub = 100; pro = 200; //pri = 300;//error,子类不可见 } }; int main(int argc, char *argv[]) { Parent parent(1,2); parent.pub = 1000; //parent.pro = 2000;//error,外部不可见 //parent.pri = 3000;//error,外部不可见 Child child(1,2); child.pub = -1000; //child.pro = -2000;//error,外部不可见 //child.pri = -3000;//error,外部不可见 child.childMethod(); return 0; }
在C++编程中,软件可重用性(software reusability)是经过继承(inheritance)机制来实现的。类的继承,是新的类从已有类获得已有的特性。从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。
继承是一种封装模型之间关系的抽象,是不一样封装模型的层次分类。函数
派生类的声明:学习
class 派生类名:[继承方式] 基类名 { 派生类成员声明; };
若是一个派生类同时有多个基类,称为多重继承;若是派生类只有一个基类,称为单继承。this
继承方式规定了如何访问基类继承的成员。继承方式有public、 private,、protected。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。继承方式以下:
A、公有继承
基类的公有成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类的私有成员。
B、私有继承
基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类的私有成员。
C、保护继承
基类的公有成员和保护成员在派生类中成了保护成员,其私有成员仍为基类的私有成员。
不一样继承方式下父类成员的访问级别以下:
继承成员的访问属性 = Max{继承方式,父类成员的访问属性}spa
使用class关键字定义派生类时,默认继承方式为private。
使用struct关键字定义派生类时,默认继承方式为public。
C++工程项目中,一般只使用public继承方式。设计
#include <iostream> using namespace std; class Parent { public: Parent(int a = 0, int b = 0, int c = 0) { pub = a; pro = b; pri = c; } public: int pub; protected: int pro; private: int pri; }; class ChildA : public Parent { public: ChildA(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; class ChildB : protected Parent { public: ChildB(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; class ChildC : private Parent { public: ChildC(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; //默认继承方式 class ChildD : Parent { public: ChildD(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; int main(int argc, char *argv[]) { ChildA childa(1000,2000); childa.pub = 0; //childa.pro = 2000;//error,外部不可见 //childa.pri = 3000;//error,外部不可见 childa.print(); ChildB childb(1001,2001); //childb.pub = 1001;//error,外部不可见 //childb.pro = 2001;//error,外部不可见 //childb.pri = 3001;//error,外部不可见 childb.print(); ChildC childc(1002,2002); //childc.pub = 1002;//error,外部不可见 //childc.pro = 2002;//error,外部不可见 //childc.pri = 3002;//error,外部不可见 childc.print(); ChildD childd(1003,2003); //childd.pub = 1003;//error,外部不可见 //childd.pro = 2003;//error,外部不可见 //childd.pri = 3003;//error,外部不可见 childd.print(); return 0; }
派生类中的成员包含两大部分,一类是从基类继承过来的,一类是本身增长的成员。从基类继承的成员表现其共性,而新增的成员体现其个性。
派生类中由基类继承而来的成员的初始化工做仍是由基类的构造函数完成,而后派生类中新增的成员在派生类的构造函数中初始化。派生类没有继承基类的构造函数和析构函数。指针
派生类的构造函数语法以下:code
派生类名::派生类名(参数总表) :基类名(参数表),内嵌子对象(参数表) { 派生类新增成员的初始化语句; //也可出如今参数列表中 }
子类构造函数必须对继承来的成员进行初始化,能够直接经过初始化列表或赋值方式进行,也能够调用父类构造函数进行初始化。
父类构造函数在子类中的调用方式:
A、默认调用,适用于无参构造函数和使用默认参数的构造函数
B、显示调用,适用于全部父类构造函数,经过初始化列表调用
#include <iostream> #include <string> using namespace std; class Parent { public: Parent() { cout << "Parent()" << endl; } Parent(string s) { cout << "Parent(string s): " << s << endl; } ~Parent() { cout << "~Parent()" << endl; } }; class Child : public Parent { public: //隐式调用父类的无参构造函数或默认参数构造函数 Child() { cout << "Child()" << endl; } //显示调用父类构造函数,若是不显示调用,默认调用 //父类的无参构造函数或默认参数构造函数 Child(string s):Parent(s) { name = s; cout << "Child(): " << s << endl; } ~Child() { cout << "~Child(): " << name << endl; } private: string name; }; int main(int argc, char *argv[]) { Child child1("bauer"); // output: // Parent(string s): bauer // Child(): bauer // ~Child(): bauer // ~Parent() return 0; }
构造函数的调用顺序不以初始化列表中的调用顺序进行,而是根据类中对成员变量声明的顺序进行调用。若是基类中没有默认构造函数(无参),那么在派生类的构造函数中必须显示调用基类构造函数,以初始化基类成员。
派生类对象建立时构造函数的调用顺序以下:
A、调用基类构造函数,调用顺序按照基类被继承时声明的顺序(从左到右);
B、调用成员变量的构造函数,调用顺序按照成员变量在类中声明的顺序;
C、调用派生类自身的构造函数。
子类构造函数中,要么显示的调用父类的构造函数(传参),要么隐式的调用。发生隐式调用时,父类要有无参构造函数或是能够包含无参构造函数的默认参数函数。子类构造函数必须对继承而来的成员进行初始化,能够经过初始化列表或者赋值的方法进行初始化,也能够经过调用父类构造函数进行初始化。
子类对象建立时构造函数的调用顺序以下:
A、调用父类的构造函数
B、调用成员变量的构造函数
C、调用类自身的构造函数
#include <iostream> using namespace std; class Parent { public: Parent() { cout << "Parent()" << endl; } Parent(string s) { cout << "Parent(string s): " << s << endl; } ~Parent() { cout << "~Parent()" << endl; } }; class Member { public: Member(int i = 0) { this->i = i; cout << "Member(int i = 0) i = " << i << endl; } ~Member() { cout << "~Member() i = " << i << endl; } private: int i; }; class Child : public Parent { public: //隐式调用父类的无参构造函数或默认参数构造函数 Child() { cout << "Child()" << endl; } //显示调用父类构造函数,若是不显示调用,默认调用 //父类的无参构造函数或默认参数构造函数 Child(string s, int i):Parent(s),member(i) { name = s; cout << "Child(): " << s << endl; } ~Child() { cout << "~Child(): " << name << endl; } private: string name; Member member; }; int main(int argc, char *argv[]) { Child childA("bauer", 10); // output: // Parent(string s): bauer // Member(int i = 0) i = 10 // Child(): bauer // ~Child(): bauer // ~Member() i = 10 // ~Parent() return 0; }
派生类拷贝构造函数的定义以下:
派生类::派生类(const 派生类& another) :基类(another),派生类新成员(another.新成员) { }
派生类中的默认拷贝构造函数会调用父类中默认或自实现拷贝构造函数,若派生类中自实现拷贝构造函数,则必须显示的调用父类的拷贝构造函数。
#include <iostream> #include <string> using namespace std; class Student { public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; } private: string name; int num; char sex; }; class Graduate : public Student { public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } void display() { print(); cout<<salary<<endl; } private: int salary; }; int main(int argc, char *argv[]) { Graduate s("bauer", 1001,'M',8000); s.display(); Graduate g = s; g.display(); return 0; }
派生类的赋值操做符重载函数的定义以下:
子类& 子类::operator=(const 子类& another) { if(this != &another) { 父类::operator =(another); // 调用父类的赋值运算符重载 this->salary = another.salary;//子类成员初始化 } return * this; }
派生类的若是没有实现赋值操做符函数,C++编译器会提供一个默认赋值操做符重载函数,默认的赋值操做符重载函数会调用父类的赋值操做符重载函数(编译器提供的默认赋值操做符重载函数或是开发者提供的赋值操做符重载函数)。
#include <iostream> #include <string> using namespace std; class Student { public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } Student& operator = (const Student& another) { if(this != &another) { name = another.name; num = another.num; sex = another.sex; } return *this; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; } private: string name; int num; char sex; }; class Graduate : public Student { public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } void display() { print(); cout<<salary<<endl; } private: int salary; }; int main(int argc, char *argv[]) { Graduate s("bauer", 1001,'M',8000); cout << endl; s.display(); cout << endl; Graduate g("lee", 1002, 'F', 9000); cout << endl; g.display(); cout << endl; g = s; g.display(); cout << endl; return 0; }
派生类若是实现了赋值操做符重载函数,则需在赋值操做符重载函数内显示地调用父类的赋值操做符重载函数。
#include <iostream> #include <string> using namespace std; class Student { public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } Student& operator = (const Student& another) { if(this != &another) { name = another.name; num = another.num; sex = another.sex; } return *this; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; } private: string name; int num; char sex; }; class Graduate : public Student { public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } Graduate& operator = (const Graduate& another) { if(this != &another) { Student::operator =(another); salary = another.salary; } return *this; } void display() { print(); cout<<salary<<endl; } private: int salary; }; int main(int argc, char *argv[]) { Graduate s("bauer", 1001,'M',8000); cout << endl; s.display(); cout << endl; Graduate g("lee", 1002, 'F', 9000); cout << endl; g.display(); cout << endl; g = s; g.display(); cout << endl; return 0; }
派生类的析构函数的功能是在对象销毁前进行一些必要的清理工做,析构函数没有类型,也没有参数。析构函数的调用顺序与构造函数相反。
析构函数只有一种,无重载,无默参。
子类对象销毁时析构函数的调用顺序以下:
A、调用类自身的析构函数
B、调用成员变量的析构函数
C、调用父类的析构函数
4、父类与子类的同名覆盖
子类定义父类中的同名成员时发生同名覆盖,规则以下:
A、子类能够定义父类中的同名成员(成员变量和成员函数)
B、子类中的成员将隐藏父类中的同名成员
C、父类中的同名成员依然存在于子类中
D经过做用域访问符访问父类中的同名成员
若是某派生类的多个基类拥有同名的成员,派生类又新增与基类同名的成员,派生类成员将shadow(隐藏)全部基类的同名成员,须要做用域的调用方式才能调用基类的同名成员。
#include <iostream> using namespace std; class Parent { public: int m_count; void print() { cout << &m_count << endl; } }; class Child : public Parent { public: int m_count; void print() { cout << &(Parent::m_count) << endl; cout << &m_count << endl; } }; int main(int argc, char *argv[]) { Parent p; p.print(); cout << &p.m_count << endl; Child child; child.print(); //子类对象的父类同名成员变量访问 cout << &child.Parent::m_count <<endl; //子类对象的父类同名成员函数访问 child.Parent::print(); cout << &child.m_count << endl; return 0; }
函数重载发生在同一做用域 ,父类和子类的同名函数不构成函数重载,属于同名覆盖,子类会隐藏父类中的同名成员。
子类能够定义父类中的同名成员,子类中的成员将隐藏父类中的同名成员,父类中的同名成员依然存在子类中,经过做用域分辨符::访问父类中的同名成员。
子类中的成员函数将隐藏父类中的同名成员函数,子类没法重载父类中同名成员函数,使用做用域分辨符能够访问父类中的同名成员函数。
#include <iostream> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } }; class Child : public Parent { public: int mi; void add(int x, int y, int z) { mi += (x + y + z); } }; int main(int argc, char *argv[]) { Parent p; p.add(1); p.add(1,2); Child child; child.add(1,2,3); //child.add(1);//error child.Parent::add(1); //child.add(1,2);//error child.Parent::add(1,2); return 0; }
赋值兼容原则是指子类对象能够做为父类对象使用。
赋值兼容原则的特色以下:
A、子类对象能够直接赋值给父类对象
B、子类对象能够直接初始化父类对象
C、父类指针能够直接指向子类对象
D、父类引用能够直接引用子类对象
当使用父类指针指向子类对象、父类引用对子类对象进行引用时,子类对象退化为父类对象,只能访问父类中定义的成员,所以能够直接访问被子类覆盖的同名成员。
#include <iostream> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } }; class Child : public Parent { public: int mi; void add(int x, int y, int z) { mi += (x + y + z); } }; int main(int argc, char *argv[]) { Child child; Parent p = child; p.add(1); p.add(1,2); Parent& rp = child; rp.add(1); rp.add(1,2); //rp.add(1,2,3);//error Parent* pp = &child; pp->add(1); pp->add(1,2); //pp->add(1,2,3);//error return 0; }
子类中重定义父类中已经存在的成员函数,称为函数重写。
函数重写是同名覆盖的一种特殊状况。
#include <iostream> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } void print() { cout << "Parent" << endl; } }; class Child : public Parent { public: int mi; //函数重写 void add(int x, int y) { mi += (x + y); } //函数重写 void print() { cout << "Child" << endl; } }; int main(int argc, char *argv[]) { Child child; Parent p = child; p.add(1); p.add(1,2); Parent& rp = child; rp.add(1); rp.add(1,2); rp.print();//Parent Parent* pp = &child; pp->add(1); pp->add(1,2); pp->print();//Parent return 0; }
C++是一种静态编译语言,在编译期间,C++编译器只能根据指针的类型判断所指向的对象。根据赋值兼容原则,C++编译器认为父类指针指向的是父类对象,父类引用是对父类对象的引用,所以编译结果只能是调用父类中定义的同名函数。