C++类的组合
什么是类的组合
类的组合就是以另一个的对象为数据成员,这种情况称之为类的组合
- 优先使用组合而不是继承
- 组合表达式的含义一部分的关系
初始化参数列表
初始化参数列表是构造函数的另一种写法
应用场景:
- 形参名和数据成员相同,避免二义性问题
- 类和组合 必须要初始化参数列表的方式写构造
- 常数据成员必须采用初始化参数列表的方式
- 继承中子类的构造函数也必须初始化参数列表的方式
初始化参数列表基本形态
构造函数名(形参1,形参2....):数据成员1(形参1),数据成员2(形参2)...
初始化参数列表其他形态
#include <iostream> #include <string> using namespace std; class MM { public: //初始化参数列表 //可以避免形参名和数据成员名字相同 MM(string name, int age) :name(name), age(age) {} MM(string name) { MM::name = name; //用类名标识一下帮助IDE去识别 } void print() { cout << name << "\t" << age << endl; } private: string name; int age; }; //初始化参数列表其他写法 string name = "initName"; int returnValue() { return 23; } class Boy { public: //无参构造函数 Boy() :name("Boy"), age(111) { } //::name标识name是全局的 Boy(int age) :name(::name), age(returnValue()) { cout << age << endl; //就近原则 } void print() { cout << name << "\t" << age << endl; } private: string name; int age; }; int main() { MM mm("name", 18); mm.print(); Boy boy; boy.print(); Boy pInt(12); pInt.print(); return 0; }
类的组合案例分析
- 类组合包含的类的对象,必须采用初始化参数列表方式调用各自类当中的构造函数去初始化
- 组合中初始化参数列表的写法
- 要通过包含的类的构造函数决定组合类的构造函数怎么写
构造函数名(形参1,形参2,形参3....):对象1(形参1,形参2),对象2(形参3)...
组合类注意问题
- 组合类必须要调用包含对象所属类的构造函数
- 形式上看不到包含对象所属类构造函数调用,必须准备无参的构造函数
#include <iostream> using namespace std; //一种的关系 //一部分:组合 class Button { public: Button() { cout << "Button" << endl; } Button(int x, int y, int w, int h) :x(x), y(y), w(w), h(h) { cout << "Button" << endl; } void Draw() { cout << "按钮..." << endl; } private: int x; int y; int w; int h; }; class Edit { public: Edit() { cout << "Edit" << endl; } Edit(int x, int y) :x(x), y(y) { cout << "Edit" << endl; } void Draw() { cout << "编辑框...." << endl; } private: int x; int y; }; class Label { public: Label() { cout << "Label" << endl; } Label(int x, int y, string text) :x(x), y(y), text(text) { cout << "Label" << endl; } void Draw() { cout << "标签:" << text << endl; } private: int x; int y; string text; }; class Window { public: //window():button(),label(),edit(){} Window() //形式上没有调用,实际构造对象,必定调用包含对象的无参构造函数 { } Window(int bx, int by, int bw, int bh, int lx, int ly, string text, int ex, int ey) :button(bx, by, bw, bh),edit(ex, ey), label(lx, ly, text) { } void Show() { button.Draw(); label.Draw(); edit.Draw(); } Button getButton() { return button; } Label getLabel() { return label; } Edit getEdit() { return edit; } private: //以其他类的对象为数据成员 //构造顺序只和此处有关,和初始化参数列表顺序无关 Button button; Label label; Edit edit; }; //另一种包含指针写法 class A { public: A(int a) :a(a) {} int geta() { return a; } private: int a; }; class B { public: B(int b) :b(b) {} int getb() { return b; } private: int b; }; class C { public: C() { pa = new A(12); pb = new B(123); } C(int a, int b) :pa(new A(a)), pb(new B(b)) {} void visitData() { cout << pa->geta() << endl; cout << pb->getb() << endl; } private: A* pa; B* pb; }; int main() { Window object; //优先构造包含对象,在构造自身对象 //object.getButton().Draw(); //object.getEdit().Draw(); //object.getLabel().Draw(); object.Show(); Window window(10, 10, 10, 10, 20, 20, "Label", 30, 30); window.Show(); C c; c.visitData(); C value(1, 2); value.visitData(); return 0; }
组合中构造和析构顺序问题
- 一般构造顺序和析构是相反
- 类的组合中,优先构造包含对象,在构造自身对象
- 类的组合中,包含对象的构造顺序只和定义顺序有关,和初始化参数列表无关
#include <iostream> using namespace std; class A { public: A() { cout << "A"; } ~A() { cout << "A" ; } }; class B { public: B() { cout << "B"; } ~B() { cout << "B"; } }; class C { public: C() { cout << "C"; } ~C() { cout << "C"; } }; class D { public: D() { cout << "D"; } //初始化参数列表写出去迷惑 ~D() { cout << "D"; } A a; //A B b; //B C c; //C //D }; int main() { { D d; } return 0; }
this指针
任何类中都存在一个this指针,this指针只允许在类中函数的函数中使用
this指针代表的是每一个对象抽象地址
基本用法
避免形参名和数据成员的名相同
#include <iostream> #include <string> using namespace std; class MM { public: MM(string name, int age); void modifyData(string name, int age) { //MM::name = name; //MM::age = age; this->name = name; this->age = age; cout << this << endl; } void print(); protected: string name; int age; }; //初始化参数列表类外也行 MM::MM(string name, int age) :name(name), age(age) { } void MM::print() { cout << this->name << "\t" << this->age << endl; } int main() { MM mm("mm", 18); mm.modifyData("MM", 28); //this=&mm; cout << "&mm:" << &mm << endl; MM girl("girl", 19); girl.modifyData("girl", 29);//this=&girl; cout << "&girl:" << &girl << endl; return 0; }
其他作用
操作对象自身做一些事情
- 返回对象本身函数
- 返回对象本身的地址
#include <iostream> #include <string> using namespace std; class MM { public: MM(string name, int age); void modifyData(string name, int age) { //MM::name = name; //MM::age = age; this->name = name; this->age = age; cout << this << endl; } MM& returnMM() { return *this; } MM* returnMMPoint() { return this; } void print(); protected: string name; int age; }; //初始化参数列表类外也行 MM::MM(string name, int age) :name(name), age(age) { } void MM::print() { cout << this->name << "\t" << this->age << endl; } int main() { MM mm("mm", 18); mm.modifyData("MM", 28); //this=&mm; cout << "&mm:" << &mm << endl; MM girl("girl", 19); girl.modifyData("girl", 29);//this=&girl; cout << "&girl:" << &girl << endl; mm.returnMM().returnMM().returnMM().returnMM().print(); mm.returnMMPoint()->returnMMPoint()->returnMMPoint()->print(); mm.print(); mm.returnMMPoint()->print(); return 0; }
类中类
类中类就是一个类定义在另一个类当中
- 掌握访问类中类中即可
- 掌握类中的函数在类实现的写法
#include <iostream> using namespace std; struct Node { int data; Node* next; Node() :next(nullptr) {} Node(int data) :data(data), next(nullptr) {} Node(int data, Node* next) :data(data), next(next) {} }; class List { public: List(); void insertData(int data); void printList() { Node* pmove = headNode->next; while (pmove != nullptr) { cout << pmove->data << " "; pmove = pmove->next; } cout << endl; } Node* begin() { return headNode; } private: Node* headNode; public: //类中类 class Iterator { public: Iterator(Node* pmove=nullptr); private: Node* pmove; }; }; List::List() { headNode = new Node; } void List::insertData(int data) { headNode->next = new Node(data, headNode->next); } //类中类的访问剥洋葱 List::Iterator::Iterator(Node* pmove):pmove(pmove) { } int main() { List list; List::Iterator it=list.begin(); list.insertData(1); list.insertData(2); list.insertData(3); list.printList(); return 0; }
小试牛刀
//自己想一个组合案案例,写一下测试代码 //准备综合类 //多个分支类 //每一个实现一下 //案例1 //给孩子起名字 //父亲类 //姓 //名 //母亲类 //姓 //名 //孩子类-->综合类 //姓 =父姓+母姓 //名 =自己传一个 //案例2 //头部描述 //头类-->综合类 //眼睛 -->string描述什么样的眼睛 //鼻子 -->string描述什么样的眼睛 //耳朵 -->string描述什么样的眼睛 //自由发挥也行 //........