友元函数
原则上,不能从声明它们的同一类外部访问该类的私有成员和受保护成员。但是,此规则不适用于“朋友”。
友元是用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。因此,functionduplicate能够访问type的不同对象的成员width和height(是私有的)Rectangle。请注意,尽管在声明中duplicate或main函数以后使用中,函数均不duplicate视为类的成员Rectangle。不是!它只是没有成员就可以访问其私有成员和受保护成员。
朋友功能的典型用例是在两个不同类之间进行的操作,这些类访问二者的私有或受保护成员。
友元类
与朋友功能类似,朋友类是一个类,其成员可以访问另一个类的私有或受保护成员:
// 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
在此示例中,classRectangle是class的朋友,Square允许其Rectangle成员函数访问的私有成员和受保护成员Square。更具体地说,Rectangle访问成员变量Square::side,该变量描述正方形的侧面。
此示例中还有一些新内容:在程序的开头,有一个空的class声明Square。这是必需的,因为classRectangle使用Square(作为member中的参数convert)并Square使用Rectangle(将其声明为朋友)。
除非特别说明,否则永远不会建立友谊:在我们的示例中,“”Rectangle不将友谊视为朋友类Square,而,则不将Square视为朋友Rectangle。因此,成员函数Rectangle可以访问的受保护成员和私有成员,Square但不能相反。当然,Square也可以将其声明为的朋友Rectangle,如果需要,可以授予此类访问权限。
友谊的另一个特性是,它们不是可传递的:除非明确指定,否则朋友的朋友不会被视为朋友。
类之间的继承
可以扩展C ++中的类,创建保留基类特征的新类。这个过程称为继承,它涉及一个基类和一个派生类:派生类继承了基类的成员,并且可以在其基础上添加自己的成员。
例如,让我们想象一系列描述两种多边形的类:矩形和三角形。这两个多边形具有某些共同的属性,例如计算它们的面积所需的值:可以使用高度和宽度(或底数)简单地描述它们。
在类的世界中可以用一个类Polygon来表示,我们可以从中得到另外两个类:Rectangle和Triangle:
该Polygon班将包含成员是两种类型的多边形常见。在我们的情况下:width和height。并且Rectangle并且Triangle将是其派生类,它们的特定特征与一种多边形不同。
从其他派生的类继承基类的所有可访问成员。这意味着,如果基类包含一个成员,A并且我们从该类派生出一个带有另一个成员B的类,则该派生类将同时包含memberA和member B。
在派生类中声明了两个类的继承关系。派生类定义使用以下语法: 哪里是派生类的名称,以及
class derived_class_name: public base_class_name
{ /…/ };
derived_class_namebase_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。类中使用
的protected访问说明符Polygon与相似private。唯一的区别实际上是在继承方面发生的:当一个类继承另一个类时,派生类的成员可以访问从基类继承的受保护成员,但不能访问其私有成员。
通过声明width和height作为protected代替private,这些成员还从派生类访问Rectangle和Triangle,而不是仅仅从成员Polygon。如果它们是公开的,则可以从任何地方访问它们。
我们可以根据哪些功能可以通过以下方式访问它们来总结不同的访问类型:
“非成员”表示从类外部进行的任何访问,例如从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),则所有基类成员都将继承为private。
例如,如果女儿是从母亲派生的课程,我们将其定义为:
class Daughter: protected Mother;
这将被设置protected为其Daughter从母亲继承的成员的限制性较低的访问级别。也就是说,是所有成员public在Mother将成为protected中Daughter。当然,这不会限制Daughter宣布自己的公共成员。这限制较少的访问级别时,才设置从继承的成员Mother。
如果没有为继承指定访问级别,则编译器将对用关键字声明的类假定为私有,对于用声明的类假定为class公共struct。
实际上,大多数C ++继承用例都应使用公共继承。当基类需要其他访问级别时,通常可以更好地将它们表示为成员变量。
从基类继承什么?
原则上,公共派生类继承对基类每个成员的访问权限,但以下情况除外:
它的构造函数和它的析构函数
其分配运算符成员(operator =)
它的朋友
它的私人成员
即使没有继承继承对基类的构造函数和析构函数的访问权限,但派生类的构造函数和析构函数也会自动调用它们。
除非另有说明,否则派生类的构造函数将调用其基类的默认构造函数(即,不带参数的构造函数)。可以使用与初始化列表中的成员变量相同的语法来调用基类的其他构造函数: 例如:
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;
}
Mother: no parameters
Daughter: int parameter
Mother: int parameter
Son: int parameter
注意创建Mother新Daughter对象时调用哪个构造函数与作为对象时调用哪个构造函数之间的区别Son。所不同的是,由于不同构造函数声明Daughter和Son:
Daughter (int a) // nothing specified: call default constructor
Son (int a) : Mother (a) // constructor specified: call this specific constructor
多重继承
一个类可以从一个以上的类中继承,只需在一个类的基类列表中(即,在冒号之后)简单指定多个用逗号分隔的基类即可。例如,如果程序有一个特定的类要在屏幕上打印,称为Output,并且我们希望我们的类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