跳转到内容

C++ 编程/结构

来自 Wikibooks,开放世界中的开放书籍

结构是一种复合数据类型,它包含不同类型和不同成员。成员可以通过其名称进行访问。结构对象的数值是对象中每个成员的数值的元组。结构也可以被看作是面向对象编程 (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, .. */ ;


注意
从技术角度来看,结构和类实际上是相同的。结构可以在任何可以使用类的地方使用,反之亦然,唯一的技术区别是类成员默认是private,而结构成员默认是public。结构可以通过在结构开头添加关键字 private 来使其像类一样工作。除此之外,这主要是一个关于约定和编程设计的区别,通常表明是从 C 语言源代码转换而来,甚至用作结构不会被继承或不会有函数成员的错误隐式指示(这种用法应该避免,并且永远不要假设)。

为什么要使用结构而不是类?

较旧的编程语言使用了一种类似的类型称为记录(例如: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; };

这个定义表明这个结构包含两个成员,名为xy。这些成员也称为实例变量,我将在稍后解释原因。

在结构定义末尾省略分号是一个常见的错误。在花括号后面放分号可能看起来很奇怪,但你会习惯的。这种语法是为了让程序员在定义结构时能够创建结构的实例。

定义了新结构后,就可以创建具有该类型的变量

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 关键字是一个隐式创建的指针,它只能在结构(或联合或类)的非静态成员函数中访问,并指向调用成员函数的对象。此指针在静态成员函数中不可用。这将在介绍联合时再次重申,在 关于类部分中提供了更深入的分析。

华夏公益教科书