首页 > 编程 > C++ > 正文

c++教程(二十: Friendship and inheritance)

2019-11-11 01:48:16
字体:
来源:转载
供稿:网友

Friend functions

原则上讲,一旦一个类被声明以后,那么这个类内部的私有与保护(PRivate and protected)成员就不可以被这个类以外的其他函数所访问。然而这一条准则对于友元类来说不适用。

友元的类或者函数的声明使用关键字“friend”。

被一个类声明了“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;}

上述中duplicate 函数就是类Rectangle的一个友元类。因此函数duplicate 是可以访问成员width和height的(虽然这两个参数是类Rectangle的私有变量)。注意的是,类duplicate中既没有声明也不包含,但是在main函数中是可以使用的。函数duplicate被认为是类Rectangle的一个成员?其实不是的,它只是可以访问类Rectangle的私有与被保护的变量而已。

友元函数的典型用例是在两个不同的类之间访问私有或受保护成员的操作。

Friend classes 友元类

同友元函数类似,友元类是一种可以访问其他类的私有与保护成员的一种类型。

// 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;}

在这个例子中,类Rectangle 就是类Square 的一个友元类,所以Rectangle的成员函数就可以访问Square的私有与保护的成员变量。更准确的说,Rectangle 访问了类Square 中的变量Square::side,而这个变量只在类square中被定义过。

在这个例子中也有一些值得注意的:在程序的开头,需要对类Square进行一个空的声明。这一点是必须的,因为类Rectangle会用到类square(在函数convert中作为参数使用),并且Square 使用了Rectangle (作为友元的声明)。

除非特别声明,否则友元关系不是对应的。在我们这个例子中,Rectangle 被认为是Square的友元,但是Square并不认为是Rectangle 的友元。因此,Rectangle 中的函数可以使用Square中的私有与保护的成员,但是反过来就不行。当然,如果Rectangle 中也可以声明Square为友元,这样的话就可以使用了。

友元的另一个属性是他们的这种关系并不能够传递,也就是说友元的友元并不是友元除非特别声明。

类间的继承

C++中的类可以扩展,创建新类保留基类的特性。这个过程称为继承,它包含基类和派生类:派生类继承基类的成员,在此基础上它可以添加自己的成员。

例如,让假设有一系列的类来描述两种多边形:矩形和三角形。这两个多边形具有一定的共同属性,如计算其面积的值:它们都可以简单地描述为高度和宽度(或基本属性)。

这可以用一个类多边形来表示,我们将得到另外两个类:矩形和三角形:

这里写图片描述

多边形类将包含两种类型的多边形的成员。在我们的例子中就是宽度和高度。矩形和三角形是其派生类,具有不同于一类多边形到另一类的特殊特征。

派生的类可以继承基类的所有可访问成员。这意味着,如果一个基类包含一个成员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;}

类的对象矩形和三角形都包含从多边形继承的成员。这些是:宽度、高度和set_values。

多边形的类说明符protected访问类似于private。对于继承来说,这两者的唯一区别在于:当类继承另一个类时,派生类的成员可以访问从基类继承的受保护成员,而不是访问其私有成员。

通过声明宽度和高度作为protected 而非private,使得这些成员也可以从派生类矩形和三角形中获得,而不仅仅是来自多边形成员。如果他们是 public,他们可以从任何地方访问。

我们可以根据功能总结不同的访问类型,可以访问它们的方式如下:

这里写图片描述

“not members”表示类外的任何访问,例如从主体、从另一个类或从函数。

在上面的示例中,矩形和三角形所继承的成员具有相同的访问权限,因为它们在基类多边形中具有相同的访问权限:

Polygon::width // protected accessRectangle::width // protected accessPolygon::set_values() // public accessRectangle::set_values() // public access

这是因为继承关系已在每个派生类中使用public关键字声明:

class Rectangle: public Polygon { /* ... */ }

这个冒号(:)后的public关键字表示从继承的类中继承的成员的最易访问级别 (在这种情况下,多边形)将来自派生类(在这种情况下,矩形)。由于公共是最容易访问的级别,通过指定关键字,派生类将继承基类中具有相同级别的所有成员。

通过protected,基类的所有公共成员在派生类中继承为受保护的protected 。相反,如果指定了最受限制的访问级别(private),所有基类成员都将继承为私有。

例如,如果女儿是来自母亲的类,我们定义为:

class Daughter: protected Mother;

Daughter将被protected为较少限制的访问级别的,它继承于Mother。也就是说,所有在Mother公开的成员都会在Daughter身上得到保护。当然,这不会限制Daughter声明的自己的public成员。只为从Mother继承的成员设置较少限制访问级别。

如果继承没有指定访问级别,编译器就会为类class假定为private 的类声明关键字,同时会为struct结构体声明为public的类关键字。

实际上,在C++大多数用例继承应该使用public继承。当基类需要其他访问级别时,通常可以更好地表示为成员变量。

从基类继承的是什么?

原则上,公开派生类继承了对基类的每个成员的访问,除了:

(1)它的构造函数和析构函数 (2)赋值操作符成员(运算符=) (3)它的友元 (4)它的private 私有成员

即使基类的构造函数和析构函数不能继承等,派生类的析构函数和构造函数也会自动调用。

除非另有说明,派生类的构造函数调用基类的默认构造函数(即没有参数的构造函数)。使用初始化列表中成员变量的相同语法,调用基类的不同构造函数是可能的:

derived_constructor_name (parameters) : base_constructor_name (parameters) {...}

例如:

// constructors and derived classes#include <iostream>using namespace std;class Mother { public: Mother () { cout << "Mother: no parameters/n"; } Mother (int a) { cout << "Mother: int parameter/n"; }};class Daughter : public Mother { public: Daughter (int a) { cout << "Daughter: int parameter/n/n"; }};class Son : public Mother { public: Son (int a) : Mother (a) { cout << "Son: int parameter/n/n"; }};int main () { Daughter kelly(0); Son bud(0); return 0;}

当创建一个新的Daughter对象,当它是Daughter对象时,请注意与Mother构造函数的调用之间的区别。不同的是由于不同的构造函数声明的Daughter和Son:

Daughter (int a) // nothing specified: call default constructorSon (int a) : Mother (a) // constructor specified: call this specific constructor

多继承

一个类可以通过指定多个基类继承多个类,以逗号分隔,在一个类的基类列表中 (即,冒号后)。例如,如果程序有一个特定的类Output在屏幕上打印输出,我们希望我们的类Rectangle 和Triangle除了继承Polygon 也继承Output的成员我们可以写:

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;}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选