十:struct结构体详解
前文
结构体本意上还是一组数据的集合,在其中,通过添加下一个地址方式,可以形成链表这样的高效删除修改数据结构。
正文
基本语法
不同于C的是,C++给予了结构体定义函数、权限等更高级用法。
1
2
3
4
5
[template-spec] struct [ms-decl-spec] [tag [: base-list ]]
{
member-list
} [declarators];
[struct] tag declarators;
其中:
template-spec 可选模板规范。即使用相应的模板。
struct struct结构体关键字。
ms-decl-spec 可选存储类规范。详细信息参阅__declspec
关键字。
tag 为结构提供的类型名称。 标记将变成结构范围内的保留字。 标记是可选项。 如果省略,则定义匿名结构。 这个参数也就是常用的命名一个结构体,如struct ListNode
。
base-list 此结构将从中派生其成员的类或结构的可选列表。 有关详细信息,请参阅基类。 每个基类或结构名称的前面可具有访问说明符(public、private、protected)和virtual
关键字。 有关详细信息,请参阅控制对类成员的访问中的成员访问表。 意思是说结构体也可通过继承方式继承其他结构体成员信息。
member-list 结构成员列表。 有关详细信息,请参阅类成员概述。 这里说明类成员意思是说在C++中结构体其实和类一样,可以自定义成员访问权限,自定义函数等。
declarators 指定结构名称的声明符列表。 声明符列表声明了一个或多个结构类型实例。 如果结构的所有数据成员是public,则声明符可包含初始值设定项列表。 初始值设定项列表在结构中很常见,因为数据成员默认为public。 有关详细信息,请参阅声明符概述。 即在创建结构体的时候声明一些变量。
最终可以通过[struct] tag declarators
自己再声明结构体变量,struct
可以省略(C++风格),而C中不可以。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct {
int a, b = 10;
const int c = 20;
protected:
int d = 200;
private:
int e = 200;
}node1, node2;
int main() {
cout << node2.a << endl;
cout << node2.b << endl;
cout << node2.c << endl;
cout << node2.d << endl;
cout << node2.e << endl;
// output: 0 10 20
// 这里a的值未初始化是随机的
// 同时这里无法访问d e,这两个是受保护的
}
定义其静态成员变量
和类一样,结构体也可定义静态变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct ListNode{
int a, b = 10;
static int d;
}node1, node2;
int ListNode::d = 30;
int main() {
cout << node2.a << endl;
cout << node2.b << endl;
cout << node2.c << endl;
cout << ListNode::d << endl;
// cout << node2.d << endl;
}
注意,静态变量必须在结构体中声明,但定义必须在外面定义,这仍然遵循static
关键字的规则, 即此变量不再属于某个实例,而是归ListNode
所有,因此无法通过node2.d
访问,这和类是一样的。
使用大括号进行初始化
并不总是需要创建后通过.成员
方式进行赋值,一个简单的语法糖是通过声明时使用大括号传参:
1
2
3
4
5
6
7
8
9
10
11
struct ListNode{
int a, b;
};
int main() {
ListNode node1{10,20};
ListNode node2{10};
cout << node1.a << endl;
cout << node2.b << endl;
}
注意此时,a b
即成员变量只能声明不能定义,编译器会将大括号内参数按顺序赋值给成员变量。
通过构造函数初始化
构造函数没有返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct ListNode{
ListNode() {}
ListNode(int a) : a(a*2) {}
ListNode(int a, int b) : a(a*3), b(b*3) {}
int a, b;
ListNode(int a, int b, int c) {
this->a = a*4;
}
ListNode(int a, int b, int c, int d) {
this->a = a*5;
}
};
int main() {
ListNode node1{10, 20};
ListNode node2{10};
ListNode node3;
ListNode node4{10, 20, 30};
ListNode node5(10, 20, 30, 40);
cout << node1.a << endl;
cout << node2.a << endl;
cout << node3.a << endl;
cout << node4.a << endl;
cout << node5.a << endl;
// output: 30 20 0 40
}
其中node1
会调用ListNode(int a, int b)
,因此输出结果是30,node2
会调用ListNode(int a)
,因此输出结果是20。
注意这里的语法:ListNode(int a) : a(a*2) {}
更像是一个语法糖,帮助给成员变量a
赋值, 但是如果写成普通形式:
1
2
3
ListNode(int a, int b, int c) {
this->a = a*4;
}
则需要通过this->a
表明是给成员变量a
赋值。
同时使用普通语法ListNode node5(10, 20, 30, 40);
也是可以调用构造函数初始化的。
使用指针变量
结构体的好处是可以通过定义名为自身的指针变量形成链表结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
int main() {
ListNode node1{10};
ListNode node2{20, &node1};
ListNode node3{30, &node2};
ListNode* head = &node3;
while(head){
cout << head->val << " ";
head = head->next;
}
ListNode* node11 = new ListNode{10};
ListNode* node22 = new ListNode{20, node11};
ListNode* node33 = new ListNode{30, node22};
ListNode* head2 = node33;
while(head2){
cout << head2->val << " ";
head2 = head2->next;
}
// ListNode* node11 = new ListNode{10};
// cout << node11->val << endl;
}
注意,其中可以通过new
关键字创建ListNode*
类型变量。
(回顾:new
关键字作用为尝试分配和初始化指定类型或占位符类型的对象或对象数组,并返回指向对象(或指向数组初始对象)的适当类型化的非零指针。即int* a = new int; *a = 10; delete a;
)
当然这里delete node11
可以释放空间。