C++初学者指南第一步---13.聚合类型

C++初学者指南第一步—13.聚合类型

1. 类型分类(简化)

基本类型void, bool, char, int, double, …
void、bool、char、int、double、…
简单聚合主要用途:对数据进行分组
聚合:可能包含一个/多个基本类型或其他兼容的聚合类型
无法控制组成类型的相互作用
简单,(编译器生成的)默认构造/析构/复制/赋值。
标准的内存布局(所有成员按照声明顺序连续布置),如果所有成员具有相同的访问控制(例如全部为public)。
更复杂的自定义类型自定义不变量和对成员相互作用的控制
限制成员访问
具有成员函数
用户自定义构造/成员初始化
用户自定义的析构 / 复制 / 赋值
可以是多态的(包含虚成员函数)

2. 如何定义和使用

示例:具有 2 个整数坐标的类型
代码段1

struct point {
  int x;  // ← "成员变量"
  int y; 
};
// 创建新对象 (在栈中)
point p {44, 55}; 
// 打印成员变量的值
cout << p.x <<' '<< p.y;  // 44 55

分配给成员变量值:(代码段2)

p.x = 10;
p.y = 20;
cout << p.x <<' '<< p.y;  // 10 20

成员变量的存储顺序与声明顺序相同。
代码段1的内存:
在这里插入图片描述
代码段2的内存:
在这里插入图片描述
运行上面代码

3. 为什么选择自定义类型/数据聚合?

接口变得更易于正确使用

  • 语义数据分组: 点,日期…
  • 避免了许多函数参数,从而减少了混乱。
  • 函数可以使用一个专门的类型来返回多个值,而不是使用多个非常量引用输出参数。

不使用则会有糟糕的接口:

void closest_point_on_line (double lx2, double ly1, double lx2i, double ly2, double px, double py, double& cpx, double& cpy) { … }
  • 许多相同类型的参数⇒容易编写错误
  • 非常量引用输出参数 ⇒ 容易出错
  • 线的内部表示也被嵌入到接口中

使用则好多了:

struct point { double x; double y; };
struct line  { point a; point b; };
point closest_point_on_line (line const& l, point const& p) { … }
  • 直接明了的接口
  • 易于正确使用
  • 如果线的内部表示更改了(例如,点+方向而不是2个点)⇒ 需要改变closest_point_on_line的实现方式,但其接口可以保持不变⇒ 大多数调用代码无需更改!

4. 聚合类型初始化

语法:

Type { arg1, arg2, …, argN }
  • 花括号括起来的成员值列表
  • 按成员声明顺序排列
enum class month {jan = 1, feb = 2,…, dec = 12};
struct date {
  int yyyy;
  month mm;
  int dd;
};
int main () {
  date today {2020, month::mar, 15};	
  // C++98的写法,依然正确:
  date tomorrow = {2020, month::mar, 16};	
}

5.混合

示例:作为 person 成员的日期

enum class month { jan=1, feb=2,, dec=12 };
struct date {   
  int yyyy;
  month mm;
  int dd;
};
struct person {
  std::string name;
  date bday;
};
int main () {
  person jlp { "Jean-Luc Picard", {2305, month::jul, 13} };
  cout << jlp.name;     // Jean-Luc Picard
  cout << jlp.bday.dd;  // 13
  date yesterday { 2020, month::jun, 16 };
  person rv = { "Ronald Villiers", yesterday };
}

运行上面代码

6. 复制

副本总是所有成员的深层拷贝!

enum class month {jan = 1,};
struct date { 
  int yyyy;  month mm;  int dd; 
};
int main () {
  date a {2020, month::mar, 7};     
  date b = a;  // deep copy of a
  b.dd = 22;   // change b
}

main 函数最后一行后的状态:
在这里插入图片描述
拷贝构造 = 创建一个具有与源相同值的新对象
拷贝赋值 = 使用源对象的值覆盖现有对象的值

struct point { int x; int y; };
point p1 {1, 2};  // 构造
point p2 = p1;    // 拷贝构造
point p3 ( p1 );  // 拷贝构造
point p4 { p1 };  // 拷贝构造
auto  p5 = p1;    // 拷贝构造
auto  p6 ( p1 );  // 拷贝构造
auto  p7 { p1 };  // 拷贝构造
p3 = p2;  // 拷贝赋值
          // (p2和p3之前都存在)

7. 值和引用的语义

值语义
= 变量是指对象本身:

  • 深拷贝:生成一个新的、独立的对象;对象(成员)的值被复制。
  • 深度赋值:使目标的值等于源对象的值。
  • 深度所有权:成员变量引用与包含对象生命周期相同的对象。
  • 基于值的比较:如果它们的值相等,则变量相等。

值语义是几乎所有编程语言中基本类型(int、double等)的默认行为,也是C++中聚合类型/用户自定义类型的默认行为。

引用语义
= 变量是对对象的引用:

  • 浅层复制:变量的副本引用同一对象。
  • 浅层赋值:赋值使变量引用不同的对象。
  • 浅层所有权:成员变量也只是引用。
  • 基于身份的比较:如果变量引用同一对象,则比较相等。

大多数其他主流语言(Java、Python、C#、Swift 等) 对用户自定义类型使用(内置)引用语义。
C++ 的情况是一致的,能够提供全面的控制:

  • 默认情况下:所有类型都采用值语义(除了C风格数组)。
  • 所有类型都可以使用可选的引用语义(通过引用或指针)来实现。

8.聚合的向量(std::vector)

值语义 ⇒

  • vector 的存储包含类型 T 的对象本身,而不仅仅是对它们的引用或指针(就像在 Java/C#/… 中一样)。
  • 如果向量对象被释放 ⇒ 包含的 T 对象也会被释放。
vector v { 0,1,2,3,4 };在这里插入图片描述
struct p2d { int x; int y; };
vector v {{1,2},{5,6},{8,9}};
在这里插入图片描述

9.最令人烦恼的解析

无法使用空括号进行对象构造,因为在C++语法中存在歧义。
在这里插入图片描述

struct A { ... };
A a();  // 声明了一个函数 'a'
        // 没有参数
        // 返回类型为 'A'
A a;    // 构造了一个 A 类型的对象
A a{}  // 也构造了一个 A 类型的对象

附上原文地址
如果文章对您有用,请随手点个赞,谢谢!^_^

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值