前言
"打牢基础,万事不愁" .C++的基础语法的学习
引入
类包含的理解和使用
类的回顾
在前面帖子中(自用)关于程序的一些概念2:类和文件简单把类分成了数据类,动作类,组合类.数据类没有"动作",成员方法只有"set"和"get"方法;比如一颗白菜;动作类有"数据"和"动作".比如一个人;组合类的属性可以是数据类对象,动作类对象,其他组合类对象,可以"指挥"做出一套"动作",比如一个餐饮部门.其实设计模式就是专门研究类与类之间关系的,还被独立成一门课(笔者如果觉得对此有些理解,再行分享).先大致对类有个了解.
类包含
一.类包含和类继承
<<C++ Prime Plus>> 6th Edition(以下称为"本书")上说的是:"类包含拥有被包含类的实现,类继承拥有被继承类的实现和接口" .读起来有些拗口,其实他们很容易区分---类包含拥有实在的数据,而类继承可理解成生成了新的数据类型,但没有数据产生.此外基类的虚方法可以有定义,派生类可以在实现的虚方法中使用基类所定义的虚方法.顺带一提.
二.类包含的条件
一个类包含其他类对象的前提,是被包含类可见. 通常在当前类包含了被包含类的头文件就可以了,如果被包含类被放在命名空间中,则需要加上using编译指令或者using声明.这个比较简单.内部类对于外部类直接可见.
三.类包含的目的
类包含可以是类型对象,指针(用以表示数组或者结点).类包含可以获得或者修改被包含数据,以得到某种结果.内置数据类型如int,double,string等没在包含数据类型之列,同样可以获得或修改.
举例:
#include<iostream>
#include<vector>
using namespace std;
class Teacher { //教师类
string name;
public:
Teacher(string na):name(na){}
void judge() { cout << name<<"批改作业" << endl; }
void showname() { cout << "名字是:" << name << endl; }
};
class Student { //学生类
string name;
public:
Student(string na) :name(na) {}
void homework() { cout << "写作业" << endl; }
void showname() { cout << "学生名字是:" << name << endl; }
};
class Class { //班类
int grade; //班是哪个年级
int classname; //班是第几班
Student* students; //班里有多个学生
vector<Teacher> teachers; //班有多个教师
public:
Class(Student* st,vector<Teacher> & te,int gr,int cl):students(st),teachers(te),grade(gr),classname(cl){}
Class() {}; //给默认构造函数,可以不写内容,不会报错会有警告未初始化变量
Student getFirstStudent() { //获得学生表里第一个学生
return students[0];
}
Teacher getFirstTeacher() { //获得老师表里第一个老师
return teachers.at(0);
}
Class gfs() { //这个函数看起来有些奇怪,迭代器里会用到访问数据,返回本类对象
Class t;
t.students = this->students;
return t;
}
};
int main(void) {
Teacher Zhang("张老师");
Teacher Lee("李老师");
vector<Teacher> teachers; //建立Teacher的容器,放入张老师和李老师
teachers.push_back(Zhang);
teachers.push_back(Lee);
Student xiaoming("小明");
Student xiaohong("小红");
Student a[]= { xiaoming,xiaohong }; //对象小明和小红放入对象数组
Student* students = a; //声明对象指针指向数组
Class sanYi = Class(students, teachers,3,1);//生成对象3年级1班
sanYi.getFirstStudent().showname();
sanYi.getFirstTeacher().judge();
}
(代码写得很简单,没有多大具体含义.只访问传入属性,而没有修改)
----说明:面向对象的编程方式和面向过程不同,但最终都是面向过程的,因为程序的运行依赖的是一条一条函数的调用.面向过程是直接调用,面向对象是先生成对象再调用.类包含也可以看作代码整合的一种方式.程序就像一条不断向前的运输带,包含的类就像一个个被放上去的货物.
打个比方:餐厅有做炒菜的,有做冷盘的,还有熬汤的师傅,分别为他们写个类A,类B,类C.现在需要一桌菜,需要这三位师傅来做.可以建立一个类Cooking去整合他们,把三位师傅包含进来,一桌菜式用一个成员方法fun1(),多种菜式可以设置多个成员方法fun2(),fun3()....
再进一步,如果现在还有一位做蒸菜的师傅D要进来,这时可以考虑几种方案.一是建立另一个类Cooking2去包含ABCD四个师傅,二是把原来的Cooking做基类,生成一个派生类CookingPlus去整合.三可以用接口管理,接口的范围可以很宽,接口里还可以调用其他接口方法. ----这段内容可以做了解,因为和设计模式有关联,笔者也在摸索,只是一说
=============================内容分割线====================================
没有用到的那个函数gfs()是迭代器里面会用到的写法,原理很简单:不同的对象对应不同的属性.虽然迭代器是容器内已经实现了的代码,不需要再去写,他的思路是:当访问了数据集合中某个数据,返回对应的迭代器对象,然后用迭代器去遍历数据,产生指针遍历数据集合写法的效果. 类包含的成员方法可以修改和访问数据(属性),生成新对象也可以用来修改和访问数据
=============================内容分割线====================================
互相包含的尝试
两个类可以互相包含吗,一个类拥有另一个类的指针.
代码思路:调用时让先生成一个类的对象first,设属性:另一个类对象指针指空;再将first作为属性,生成第二个类对象second;此时再将对象first的指针属性设为second;
======================能通过编译无法运行====================================
以下代码不可用
#include<iostream>
using namespace std;
class TmpTwo { //类TmpTwo
TmpOne* to;
string name;
public:
TmpTwo(TmpOne * t,string na):to(t),name(na){}
TmpTwo() { //默认属性:指针指空
to= 0;
name="good";
};
string getName(){ return name; } //get姓名
void setName(const string& na) { name = na; } //set姓名
TmpOne* getTO() const { return to; } //获得指针属性值
void setTO(TmpOne* t) {to=t ; } //设置指针属性值
};
class TmpOne {
TmpTwo* tt;
int age;
public:
TmpOne(TmpTwo* t, int ag) :tt(t), age(ag) {}
TmpOne() { //默认属性:指针指空
tt = 0;
age = 0;
};
int getAge() const { return age; } //get年龄
void setAge(int ag) { age = ag; } //set年龄
TmpTwo* getTt() const { return tt; }
void setTt(TmpTwo* t) { tt = t; }
};
int main(void) {
TmpOne* to=new TmpOne(); //生成TmpOne*对象to,此时属性TmpTwo*指空
TmpTwo* tt=new TmpTwo(to, "good"); //将to传入,生成TmpTwo*对象tt
to->setTt(tt); //设置对象to的TmpTwo*属性为tt
to->setAge(18);
cout << "从TmpTwo访问到的数据为:" << tt->getTO()->getAge() << endl;
}
======================能通过编译无法运行====================================
这种写法有点像"先有鸡还是先有蛋",一般不认为这是正常逻辑,所以编译器也不让通过
小结
对类包含的一些分析