跳转到内容
新建笔记

C 与 C++ 的 struct:访问、构造、匿名成员和 class

在 C++ 中,struct 与 class 都定义类类型,可以具有构造、析构、成员函数、继承和模板。两个关键的默认差异是:struct 的成员和基类访问默认为 public,class 的这两项默认为 private。

C 的 struct 则是另一门语言中的结构体类型。不要把“C++ 的 struct”理解为只能保存 C 风格数据的简化 class。下面分别使用 C11 和 C++17 编译示例。

能力C11 structC++17 struct
数据成员支持;没有 public/private 访问控制支持;默认 public,可改访问区段
成员函数、构造和析构不支持;可保存函数指针作为数据支持,与 class 一样
继承和虚函数没有对应的语言机制支持;默认 public 继承
类型名称通常写 struct Point,也可用 typedef 别名通常直接写 Point
模板不支持 C++ 模板可以定义类模板
初始化聚合初值、赋值等 C 规则可聚合初始化,也可调用构造函数,取决于类型定义

C 的结构体标签与普通标识符使用不同的名字空间,这里的“名字空间”是 C 标识符分类规则,不是 C++ 的 namespace 语法。typedef struct Point Point; 可以在 C 中建立普通类型别名,之后便能写 Point p;。C 结构体声明

完整 C11 程序:标签、别名与函数指针

跳转到“完整 C11 程序:标签、别名与函数指针”
#include <assert.h>
struct Point {
int x;
int y;
};
typedef struct Point Point;
struct Calculator {
int (*apply)(int, int);
};
int add(int left, int right) { return left + right; }
struct Envelope {
struct { int x; int y; }; /* C11 的匿名结构体成员 */
int kind;
};
int main(void) {
struct Point first = {10, 20};
Point second = first;
second.x = 30;
assert(first.x == 10 && second.x == 30);
struct { int x; int y; } point = {3, 4};
assert(point.x + point.y == 7);
struct Calculator calculator = {add};
assert(calculator.apply(2, 3) == 5);
struct Envelope envelope = {{5, 6}, 1};
assert(envelope.x == 5 && envelope.y == 6 && envelope.kind == 1);
return 0;
}

Calculator 中的 apply 是一个函数指针字段,不是 C++ 那样的成员函数;它没有自动传入的 this。结构体本身不能把一个函数类型直接当作数据成员。

无名类型,不等于匿名成员

跳转到“无名类型,不等于匿名成员”

struct { int x; int y; } point; 定义了一个没有类型标签、但变量名叫 point 的对象,标准 C 与 C++ 都可以使用这个形式。原笔记将它概括成“C 可能需要扩展支持”并不准确。

上例 Envelope 中的 struct { int x; int y; }; 则没有成员名,允许直接访问 envelope.x;这是 C11 的匿名结构体成员。它不是标准 C++17 的对应功能,某些 C++ 编译器仅作为扩展接受。C++ 的匿名 union 又有单独规则,不能与匿名 struct 混为一谈。GCC 对 ISO C11 无名字段的说明

完整 C++17 程序:默认访问、构造与多态

跳转到“完整 C++17 程序:默认访问、构造与多态”
#include <cassert>
#include <type_traits>
struct MyStruct {
int value;
explicit MyStruct(int input) : value(input) {}
int doubled() const { return value * 2; }
};
class MyClass {
int value_; // 默认 private
public:
explicit MyClass(int input) : value_(input) {}
int value() const { return value_; }
};
struct Base {
virtual ~Base() = default;
virtual int value() const { return 1; }
};
struct PublicChild : Base { // 默认 public 继承
int value() const override { return 2; }
};
class PrivateChild : Base {}; // 默认 private 继承
template<class T>
struct Box {
T data;
};
int main() {
MyStruct object{10};
object.value = 12;
assert(object.doubled() == 24);
assert(MyClass{10}.value() == 10);
static_assert(std::is_convertible_v<PublicChild*, Base*>);
static_assert(std::is_base_of_v<Base, PrivateChild>);
static_assert(!std::is_convertible_v<PrivateChild*, Base*>);
PublicChild child;
const Base& base = child;
assert(base.value() == 2);
Box<int> box{42};
assert(box.data == 42);
struct { int x; int y; } point{3, 4};
assert(point.x + point.y == 7);
}

是否写 struct 不限制多态能力,使用 class 也不要求所有成员都私有;显式的 public/private/protected 声明会覆盖默认值。默认成员访问、默认基类访问

虚函数的常见实现使用虚表,但具体虚表布局不是这两个关键字提供的标准保证。跨 C/C++ 边界共享数据时,还必须约定字段类型、对齐、填充与 ABI;把 class 改写成 struct,并不会自动获得可移植的文件或网络二进制格式。