原则上,类的私有成员和受保护成员不能从声明它们的同一类的外部访问。然而,这条规则不适用于“朋友”。
朋友是用friend关键字声明的函数或类。
如果声明为类的朋友,非成员函数可以访问类的私有成员和受保护成员。 这是通过在类中包含外部函数的声明,并在其前面加上关键字friend来实现的:
// friend functions #include <iostream> using namespace std; class Rectangle { int width, height; public: Rectangle() {} Rectangle (int x, int y) : width(x), height(y) {} int area() {return width * height;} friend Rectangle duplicate (const Rectangle&); }; Rectangle duplicate (const Rectangle& param) { Rectangle res; res.width = param.width*2; res.height = param.height*2; return res; } int main () { Rectangle foo; Rectangle bar (2,3); foo = duplicate (bar); cout << foo.area() << '\n'; return 0; } |
24 |
这个duplicate 的函数是Rectangle类的朋友。因此, 函数duplicate能够访问不同Rectangle对象的成员width 和height (它们是私有的)。 请注意,无论是在duplicate的声明中,还是在main中以后的使用中, 函数duplicate都被认为是Rectangle类的成员。 但是!它只是可以访问Rectangle的私有和受保护的成员,而不是成员。
朋友函数的典型用例是两个不同类之间的操作.即访问两个类的私有成员或受保护成员的时候。
与朋友函数类似,朋友类的成员可以访问另一个类的private或protected成员:
// friend class #include <iostream> using namespace std; class Square; class Rectangle { int width, height; public: int area () {return (width * height);} void convert (Square a); }; class Square { friend class Rectangle; private: int side; public: Square (int a) : side(a) {} }; void Rectangle::convert (Square a) { width = a.side; height = a.side; } int main () { Rectangle rect; Square sqr (4); rect.convert(sqr); cout << rect.area(); return 0; } |
16 |
在这个例子中,Rectangle类是Square类的朋友,它允许Rectangle的成员函数访问Square 的私有成员和受保护成员。更具体地说,Rectangle访问成员变量Square::side, 该变量描述了正方形的边。
在这个例子中还有一些新的东西:在程序的开始,有一个Square类的空声明。 这是必须的,因为类Rectangle使用Square(作为成员转换中的参数), 而Square使用Rectangle(声明它为朋友)。
在我们的例子中,Rectangle被Square认为是一个朋友类,但Square不被Rectangle认为是朋友。 因此,Rectangle的成员函数可以访问Square的受保护成员和私有成员,反之则不行。 当然,Square也可以被声明为Rectangle的朋友,如果需要,可以授予这样的访问权限.
朋友的另一个特点是不能传递:朋友的朋友不被认为是朋友,除非明确说明。
c++中的类可以扩展,创建保留基类特征的新类。这个过程称为继承, 涉及基类和派生类:派生类继承基类的成员,并可以在基类之上添加自己的成员。
例如,让我们设想用一系列类来描述两种多边形:矩形和三角形。 这两个多边形具有某些共同属性,如计算其面积所需的值: 它们都可以用高度和宽度(或底边)简单地描述。
这可以用一个类Polygon来表示,我们可以从它派生出另外两个类:Rectangle和Triangle:
Polygon类将包含两种类型的多边形的共同成员。 在我们的例子中是:width 和height。 Rectangle 和Triangle 将是它的派生类, 它们具有各自的特定特性。
派生自其他类的类继承基类的所有可访问成员。这意味着,如果基类包含一个成员a, 我们从它派生出一个类,并使用另一个名为B的成员,派生类将同时包含成员a和成员B。
两个类的继承关系在派生类中声明。派生类定义使用以下语法:
class derived_class_name: public base_class_name { /*...*/ }; |
其中derived_class_name是派生类的名称,base_class_name是它所基于的类的名称。 public 访问修饰符可以被其他任何一个访问修饰符(protected 或private)替换。 此访问修饰符限制从基类继承的成员的最低可访问级别:具有更低可访问级别的成员将继承此级别, 而具有相等或更高限制的访问级别的成员将在派生类中保留其限制级别。
// derived classes #include <iostream> using namespace std; class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b;} }; class Rectangle: public Polygon { public: int area () { return width * height; } }; class Triangle: public Polygon { public: int area () { return width * height / 2; } }; int main () { Rectangle rect; Triangle trgl; rect.set_values (4,5); trgl.set_values (4,5); cout << rect.area() << '\n'; cout << trgl.area() << '\n'; return 0; } |
20 10 |
Rectangle和Triangle类的对象都包含从Polygon继承来的成员。 它们是:width, height和set_values。
在Polygon类中使用的受保护的访问修饰符(protected )类似于private。它唯一的区别实际上发生在继承上: 当一个类继承另一个类时,派生类的成员可以访问从基类继承的受保护成员,但不能访问其私有成员。
通过声明width 和height 为protected而不是private,这些成员也可以从派生类Rectangle和Triangle访问, 而不只是从Polygon的成员访问。如果它们是公开的,派生类可以从任何地方访问它们。
我们可以将不同的访问类型按照函数可以访问的方式归纳如下:
关系 | public | protected | private |
同一类的成员 | yes | yes | yes |
派生类的成员 | yes | yes | no |
普通关系 | yes | no | no |
其中"普通关系"表示来自类外部的任何访问,如来自main、来自另一个类或来自函数。
在上面的例子中,Rectangle和Triangle继承的成员拥有与它们在基类Polygon中相同的访问权限:
Polygon::width // protected access Rectangle::width // protected access Polygon::set_values() // public access Rectangle::set_values() // public access |
这是因为继承关系已经在每个派生类上使用public关键字声明:
class Rectangle: public Polygon { /* ... */ } |
冒号(:)之后的public关键字表示从它后面的类(在本例中是Polygon)继承的成员将从派生类 (在本例中是Rectangle)继承的最可访问级别。 因为public是最低访问的级别,通过指定这个关键字,派生类将继承与基类中具有相同级别的所有成员。
使用protected时,基类的所有公共成员都被继承为派生类中的protected成员。 相反,如果指定了最严格的访问级别(private),则所有基类成员都将作为私有继承。
例如,如果daughter我们定义为:
class Daughter: protected Mother; |
这将为从Mother那里继承的Daughter成员设置受保护的访问级别。 也就是说,所有在Mother身上公开的成员都会在Daughter身上受到保护。 当然,这并不会限制Daughter声明自己的公共成员。仅为继承自Mother的成员设置限制较低的访问级别。
如果没有为继承指定访问级别,则编译器对用关键字class声明的类假定为private, 对用struct声明的类假定为public。
实际上,c++中的大多数继承用例应该使用公共继承。当基类需要其他访问级别时,它们通常可以更好地表示为成员变量。
即使对基类的构造函数和析构函数的访问不是继承的,它们也会被派生类的构造函数和析构函数自动调用。
除非另有说明,派生类的构造函数调用基类的默认构造函数(即不带参数的构造函数)。 可以调用基类的不同构造函数,使用与初始化列表中初始化成员变量相同的语法:
derived_constructor_name (parameters) : base_constructor_name (parameters) {...}请注意,在创建新的Daughter对象时调用的是Mother的构造函数, 而在创建Son对象时调用的也是Mother的构造函数。不同之处在于Daughter和Son的构造函数声明不同:
Daughter (int a) // nothing specified: call default constructor Son (int a) : Mother (a) // constructor specified: call this specific constructor |
一个类可以继承多个类,只需在类的基类列表(即 冒号之后)中指定多个基类,用逗号分隔。 例如,如果程序有一个特定的类输出到屏幕上, 我们希望我们的类Rectangle和Triangle除了Polygon之外也继承它的成员,我们可以这样写:
class Rectangle: public Polygon, public Output; class Triangle: public Polygon, public Output; |
// multiple inheritance #include <iostream> using namespace std; class Polygon { protected: int width, height; public: Polygon (int a, int b) : width(a), height(b) {} }; class Output { public: static void print (int i); }; void Output::print (int i) { cout << i << '\n'; } class Rectangle: public Polygon, public Output { public: Rectangle (int a, int b) : Polygon(a,b) {} int area () { return width*height; } }; class Triangle: public Polygon, public Output { public: Triangle (int a, int b) : Polygon(a,b) {} int area () { return width*height/2; } }; int main () { Rectangle rect (4,5); Triangle trgl (4,5); rect.print (rect.area()); Triangle::print (trgl.area()); return 0; } |
20 10 |