一个类作为另一个类的数据成员
写程序的时候希望一个类能够访问另一个类的数据成员(protected, private),例如类A要访问类B中的数据成员。
1.保持封装性
可以通过在被访问的类(B)中定义访问权限为public的函数,用来返回数据成员的引用。
2.继承
当一个派生类从父类继承时,父类的所有成员就成为子类的成员,因此可以将被访问类作为父类被继承。要注意此时对父类成员的访问状态由继承时使用的继承限定符决定。
父类权限 | 继承方式 | ||
private | protected | public | |
private | private | private | private |
protected | private | protected | protected |
public | private | protected | public |
3.友元
申明要访问类为被访问类的友元。例如类A要访问类B中的数据成员,在B的构造函数加上friend class A; 声明A是B的友元,可以直接访问到B中的所有成员。举个例子就是:
class B { private: int age; protected: int money; public: int gender; B() { age = 20; money = 1000; gender = 1; } friend class C; int getAge(){ return age; } int getMoney(){ return money; } int getGender(){ return gender; } }; class A { public : A() {}; int compute(D &testd) { return ( 25 - testd.age) * testd.money ; } }; int main() { A testa; B testb; testa.compute(testa); /* 输出结果为(25-20)*1000 */ }
此时B的一个对象作为A某函数成员的形参,原本通过对象是无法访问到B中的private和protected成员。还可以声明B作为A的数据成员,这样也能访问到B中所有成员。
当使用后一种方法,一个类作为另一个类的数据成员。需要注意:
如果一个类B的对象作为另一个类A的数据成员,则在类A的对象创建过程中,调用其构造函数的过程中,数据成员(类B的对象)会自动调用类B的构造函数。 但应注意:如果类B的构造函数为有参函数时,则在程序中必须在类A的构造函数的括号后面加一“:”和被调用的类B的构造函数,且调用类B的构造函数时的实参值必须来自类A的形参表中的形参。这种方法称为初始化表的方式调用构造函数。
如:以上面定义的类A为例,在对类A的对象进行初始化时,必须首先初始化其中的子对象,即必须首先调用这些子对象的构造函数。因此,类A的构造函数的定义格式应为:
A:: A(参数表0):成员1(参数表1),成员2(参数表2),…,成员n(参数表n) { ……}
其中,参数表1提供初始化成员1所需的参数,参数表2提供初始化成员2所需的参数,依此类推。并且这几个参数表的中的参数均来自参数表0,另外,初始化X的非对象成员所需的参数,也由参数表0提供。
在构造新类的对象过程中,系统首先调用其子对象的构造函数,初始化子对象;然后才执行类X自己的构造函数,初始化类中的非对象成员。对于同一类中的不同子对象,系统按照它们在类中的说明顺序调用相应的构造函数进行初始化,而不是按照初始化表的顺序。
另外:访问的时候还有几个前提条件
1.被引用的变量所在类必须被完整地定义,而不是只有前向声明(例如只是一行class A;);
2.被引用的变量必须是引用处可以访问的。
2.1.访问变量的语句所在的类被声明为被访问的变量所在类的友元类;
2.2.访问变量的语句所在的函数被声明为被访问变量所在类的友元函数;
2.3.被访问变量被public修饰,且访问变量的语句所在的类不是被访问变量所在的类的private继承派生类;
2.4.被访问变量被protected修饰,且访问变量的语句所在的类是被访问变量所在的类的public继承派生类;
2.5.访问变量的语句所在的类定义在被访问变量所在的类的内部。
现在假设满足以上前提条件的A类的某个成员函数或成员初始化的语句要引用B类的成员m,
那么
1.当m是A类的静态成员时,可以通过A::m引用;
2.当m是A类的非静态成员,且对象a是类A的实例时,可以通过a.m引用。
以上基本包含了所有的情况。