C++ 编程/结构
结构是一种复合数据类型,它包含不同类型和不同成员。成员可以通过其名称进行访问。结构对象的数值是对象中每个成员的数值的元组。结构也可以被看作是面向对象编程 (OOP) 中对象范式的一种简单实现。Astruct就像一个class 除了默认访问权限(类默认访问权限为 private,结构默认访问权限为 public)。C++ 还保证只包含 C 类型的结构等效于相同的 C 结构,从而允许访问旧版 C 函数,它可以(但可能不会)也有构造函数(并且必须有它们,如果一个模板类被用在一个struct)中,与类一样,如果结构没有用户声明的析构函数,编译器会隐式声明一个析构函数。结构还将允许 运算符重载.
结构的定义为
struct myStructType /*: inheritances */ {
public:
// declare public members here
protected:
// declare protected members here
private:
// declare private members here
};
可选关键字 public:、protected:、private: 声明以下成员的保护状态。它们可以以任何顺序放置,每个关键字可以出现多次。如果没有给出保护状态,则成员为 public。(私有或受保护的成员只能被结构的方法或友元访问;在后面的章节中解释)。
由于它在 C 中不受支持,因此在 C++ 中使用继承的结构并不常见,即使它们与类一样受支持。更具特色的方面是结构可以具有两种身份:一种是与类型相关的,另一种是与特定对象相关的。公共访问标签有时可以忽略,因为结构对成员函数和字段的默认状态是公共的。
类型为 myStructType 的对象是使用声明的
/* struct */ myStructType obj1 /* , obj2, ... */;
在开头重复关键字 struct 是可选的。
可以在结构定义中直接定义对象,而不是使用结构类型的名称
struct { /*members*/ } obj1 /*, obj2, .. */ ;
- 为什么要使用结构而不是类?
较旧的编程语言使用了一种类似的类型称为记录(例如:COBOL、FORTRAN),它在 C 中作为 struct 关键字实现。因此,C++ 使用结构来遵守 C 的传统(代码和程序员)。结构更易于程序员和编译器管理。应该使用一个struct对于 POD (PlainOldData) 类型,它们没有方法,并且所有数据成员都是public. struct可能在默认情况下使用公共继承(最常见的一种)以及public访问(如果先列出公共接口,这就是你想要的)是预期效果。使用一个class,你通常需要在两个地方插入关键字 public,但没有真正的好处。最终这只是一个约定问题,程序员应该能够习惯它。
- 点对象
作为一个简单的复合结构示例,考虑数学点概念。从某种程度上来说,一个点是两个数字(坐标),我们将其作为一个整体对待。在数学符号中,点通常用括号写,逗号分隔坐标。例如,(0, 0) 表示原点,(x, y) 表示从原点向右 x 个单位,向上 y 个单位的点。
表示点的自然方法是使用两个双精度浮点数。结构或struct是将这两个值组合成复合对象的解决方案之一。
// A struct definition:
struct Point { double x, y; };
这个定义表明这个结构包含两个成员,名为x和y。这些成员也称为实例变量,我将在稍后解释原因。
在结构定义末尾省略分号是一个常见的错误。在花括号后面放分号可能看起来很奇怪,但你会习惯的。这种语法是为了让程序员在定义结构时能够创建结构的实例。
定义了新结构后,就可以创建具有该类型的变量
struct Point blank;
blank.x = 3.0;
blank.y = 4.0;
第一行是传统的变量声明:blank 的类型是 Point。接下来的两行初始化结构的实例变量。这里使用的“点符号”类似于在对象上调用函数的语法,如fruit.length()。当然,一个区别是函数名后面总是跟着一个参数列表,即使它是空的。
像往常一样,变量 blank 的名称出现在框外面,它的值出现在框里面。在这种情况下,该值是一个包含两个命名实例变量的复合对象。
- 访问实例变量
可以使用与写入它们相同的语法来读取实例变量的值
double x = blank.x;
表达式blank.x表示“转到名为 blank 的对象,获取名为 x 的成员的值”。在这种情况下,我们将该值赋给名为x的局部变量。请注意,名为x的局部变量与名为x的实例变量之间没有冲突。点符号的目的是明确地识别你所指的变量。
可以使用点符号作为任何表达式的部分,因此以下内容是合法的。
cout << blank.x << ", " << blank.y << endl;
double distance = sqrt(blank.x * blank.x + blank.y * blank.y);
第一行输出 3, 4;第二行计算值为 5。
- 对结构的操作
我们在其他类型上使用的大多数运算符,例如数学运算符(+, %等)和比较运算符(==, >等),不适用于结构。实际上,可以为新类型定义这些运算符的含义,但我们在这本书中不会这样做。
另一方面,赋值运算符对结构有效。它可以用两种方式使用:初始化结构的实例变量或将一个结构的实例变量复制到另一个结构。初始化看起来像这样
Point blank = { 3.0, 4.0 };
花括号中的值依次赋给结构的实例变量。因此,在这种情况下,x获取第一个值,y 获取第二个值。
不幸的是,这种语法只能用于初始化,而不能用于赋值语句。因此,以下是非法的。
Point blank;
blank = { 3.0, 4.0 }; // WRONG !!
你可能想知道为什么这个完全合理的语句应该是非法的,而且没有很好的答案。(但是,请注意,自 1999 年以来,C 中存在类似的语法,并且正在考虑将其可能包含在未来的 C++ 中)。
另一方面,将一个结构赋给另一个结构是合法的。例如
Point p1 = { 3.0, 4.0 };
Point p2 = p1;
cout << p2.x << ", " << p2.y << endl;
该程序的输出为 3, 4
。
- 结构作为函数参数和返回类型
可以编写接受或返回结构的函数。例如,findCenter 接受一个 Rectangle 作为参数,并返回一个包含 Rectangle 中心坐标的 Point
struct Rectangle {
Point corner;
double width, height;
};
Point findCenter (const Rectangle& box)
{
double x = box.corner.x + box.width/2;
double y = box.corner.y + box.height/2;
Point result = {x, y};
return result;
}
要调用此函数,必须传递一个 Rectangle 作为参数,并将返回值赋给一个 Point 变量
Rectangle mybox = { {10.0, 0.0}, 100, 200 };
Point center = findCenter (mybox);
printPoint (center);
该程序的输出为 (60, 100)。
请注意,Rectangle 正在通过引用传递给函数 findCenter(在 chapter 函数中解释),因为这比复制整个结构(如果按值传递会发生这种情况)更有效。引用声明为常量,这意味着函数 findCenter 不会修改参数box,尤其是调用者的mybox 将保持不变。
- 指针和结构
结构也可以由指针指向并存储指针。规则与任何基本数据类型相同。指针必须声明为指向结构的指针。
结构也可以嵌套,因此结构的有效元素也可以是另一个结构。
//of course you have to define the Point struct first!
struct Rectangle {
Point upper_left;
Point upper_right;
Point lower_left;
Point lower_right;
};
this 关键字是一个隐式创建的指针,它只能在结构(或联合或类)的非静态成员函数中访问,并指向调用成员函数的对象。此指针在静态成员函数中不可用。这将在介绍联合时再次重申,在 关于类部分中提供了更深入的分析。