写了这么久C++,你的引用成员用对了吗?

开发
你可能会问:"真的可以在类里放引用吗?" 当然可以啦!这就像是给类一个永远忠实的小跟班,一旦确定关系,就会一直跟着你到天涯海角!

嗨!小伙伴们,今天我们来聊一个有趣的话题 - C++类里的引用成员!你可能会问:"真的可以在类里放引用吗?" 当然可以啦!这就像是给类一个永远忠实的小跟班,一旦确定关系,就会一直跟着你到天涯海角!

基础示例

想象一下,你养了一只超级黏人的小猫咪 🐱,它总是寸步不离地跟着你转悠。这种形影不离的关系,在C++中用引用成员来表达再合适不过啦!就像是给小猫咪和主人之间系上了一根永远都解不开的红线 ❤️

class Person {
    string name;
public:
    Person(string n) : name(n) {}
    string getName() { return name; }
};

瞧瞧我们的小猫咪类 🌟,它有个特别之处 - 通过引用成员owner永远记住了它的主人是谁:

class Cat {
    string name;
    Person& owner;  // 这个引用就像是小猫咪的定位器,永远指向主人 🎯
public:
    // 用初始化列表给小猫咪"认主" 🤝
    Cat(string n, Person& p) : name(n), owner(p) {}
    
    void followOwner() {
        cout << name << " 正在屁颠屁颠地跟着 " << owner.getName() << " 呢~ 🐾" << endl;
    }
};

要记住哦,引用成员就像是出生就定下的缘分,一旦在构造时确定了,就再也改变不了啦!所以在初始化的时候一定要用初始化列表,就像是在小猫咪"出生证明"上盖章一样重要呢!

来试试看吧!

让我们一起来看看这段可爱的代码是怎么运作的:

int main() {
    // 首先我们需要一位温柔的主人 👩
    Person alice("Alice");
    
    // 然后来只黏人的小猫咪,从此形影不离~ 🐱
    Cat kitty("Kitty", alice);
    
    // 瞧瞧!小猫咪马上就跟着主人到处溜达啦 🎀
    kitty.followOwner();  // 输出:Kitty正在屁颠屁颠地跟着Alice呢~ 🌈
}

就是这么简单!就像魔法一样,通过引用的力量,我们的小猫咪从此就和主人绑定在一起啦!这就是C++引用成员的魅力所在 - 简单、直接,还特别有爱。记住哦,这种深厚的"猫主情"一旦建立,就像真正的爱情一样,天长地久,永不分离!

重要注意事项

使用引用成员时要注意这几点(我们把严肃的规则说得轻松一点):

  • 就像小猫出生必须有主人一样,引用成员必须在"出生"(构造)时就被初始化。所以默认构造函数是不行的!
  • 引用成员的初始化必须在构造函数的初始化列表中完成,不能在构造函数体内进行。就像这样:
Cat(string n, Person& p) 
    : name(n), owner(p)  // ✅ 正确的方式
{ 
    // owner = p;  ❌ 在这里初始化引用成员是不行的!
}

为什么一定要在初始化列表中初始化引用成员呢?这是因为:

  • 引用一旦声明就必须立即初始化,不能先声明后赋值。这是C++语言的基本规则,引用必须在声明时就绑定到一个对象。
  • 当进入构造函数体之前,所有成员变量都已经完成构造。这意味着在构造函数体内进行的任何赋值操作都不是初始化,而是对已经初始化的对象进行修改。
  • 在构造函数体内的赋值操作实际上是在试图改变引用所指向的对象,而不是在进行初始化。然而,引用一旦绑定到一个对象,就不能再改变其绑定关系。
  • 这就像小猫咪必须在"出生"时就认定主人,而不是先生出来,再决定跟谁走。引用的这种特性确保了对象间的关系在对象生命周期内保持不变。

记住:引用成员特别适合表达"永久关联"的关系,比如我们例子中的猫和主人。但要谨慎使用,因为这种关系一旦建立就不能改变啦!

初始化列表

嘿!让我们来聊聊初始化列表这个有趣的话题吧!想象一下,如果你是一位魔法师,你有两种方式来召唤你的宠物:

  • 第一种是直接用魔法让它瞬间出现(这就像初始化列表)🪄,"啪"的一下,你的小伙伴就活灵活现地站在你面前啦!
  • 第二种方式嘛,就像是先召唤出一个"空壳",然后再往里面注入生命力(这就像在构造函数体内赋值)。显然,第一种方式更简单直接,对不对?

以下是一个简单的例子,展示了初始化列表和构造函数体内赋值的区别:

class Example {
    int value;
public:
    // 使用初始化列表
    Example(int v) : value(v) {
        // 这里不需要再赋值
    }

    // 在构造函数体内赋值
    Example(int v) {
        value = v;  // 先默认构造,再赋值
    }
};

在上面的例子中,使用初始化列表的方式更高效,因为它避免了不必要的默认构造和赋值操作。

所以啊,使用初始化列表不仅仅是为了遵守规则,它还能让我们的程序跑得更快呢!就像是坐上了特快列车,"嗖"的一下就到站了!这对于引用成员来说尤其重要,因为它们就像是害羞的小朋友,一定要在"出生"的那一刻就认定好自己的好朋友 

至于普通的成员变量嘛,虽然它们没那么害羞,可以在"出生"后再交朋友,但是!如果能一开始就交到好朋友,何乐而不为呢?这样不仅能让我们的程序跑得更快,还能避免一些不必要的麻烦,就像是省去了"相亲"的过程,直接就找到了真爱一样!

记住哦,在C++的世界里,初始化列表就像是一个温暖的魔法口袋,能让我们的对象们快速又开心地诞生!让我们一起用这个小魔法,创造出更多精彩的程序吧!

  • 初始化列表的优势:使用初始化列表不仅是语法上的要求,对于引用成员来说,它还可以提高性能。因为在初始化列表中,成员变量是直接构造的,而不是先默认构造再赋值。
  • 普通成员变量的初始化:普通成员变量可以在初始化列表中初始化,也可以在构造函数体内赋值。然而,使用初始化列表通常是更好的选择,尤其是对于类类型的成员,因为它避免了不必要的默认构造和赋值操作。

通过这些补充,我们可以更好地理解为什么引用成员必须在初始化列表中初始化,以及这种方式的优势所在。希望这些信息能帮助你更深入地理解C++中的引用成员!

什么是"直接构造"?

让我们用一个生动的例子来理解什么是"直接构造"以及它为什么更高效!

想象你在玩积木游戏,你有两种方式来建造你想要的东西:

  • 直接构造:直接用积木搭建成你想要的形状
  • 先默认构造再赋值:先随便搭个样子,然后再拆掉重建

来看个具体的例子:

class MyClass {
    int value;
public:
    // 构造函数
    MyClass(int v) : value(v) { }  // 直接构造
    
    // 默认构造函数
    MyClass() : value(0) { }
    
    // 赋值操作符
    MyClass& operator=(int v) {
        value = v;
        return *this;
    }
};

class Container {
    MyClass obj;
public:
    // 方式1:直接构造 - 只调用一次构造函数
    Container(int x) : obj(x) { }  // ✅ 更高效
    
    // 方式2:先默认构造,再赋值 - 调用默认构造函数后还要调用赋值操作符
    Container(int x) {
        obj = x;  // ❌ 效率较低
    }
};

两种方式的区别:

(1) 直接构造(使用初始化列表):

  • 就像直接把积木搭建成想要的形状
  • 只执行一次构造操作
  • 内存中直接创建目标值

(2) 先默认构造再赋值:

  • 像是先搭个空房子,再进行装修
  • 先调用默认构造函数(创建值为0的对象)
  • 然后再调用赋值操作符(修改为目标值)
  • 执行了两次操作,效率较低

所以说,直接构造就是"一步到位"地创建对象,避免了不必要的中间步骤。特别是对于引用成员这种必须立即初始化的情况,使用初始化列表进行直接构造是唯一的选择!

使用建议

引用成员最适合表达对象之间的固定关联关系。在使用时要考虑:

  • 被引用对象的生命周期必须长于包含引用的对象
  • 确保关联关系确实需要是永久的
责任编辑:赵宁宁 来源: everystep
相关推荐

2020-12-07 11:05:21

HttpClient代码Java

2020-02-15 15:33:55

Python如何运作

2022-08-21 14:00:11

消息中间件MQ

2021-11-08 10:00:19

require前端模块

2021-07-21 10:10:14

require前端代码

2022-01-25 12:41:31

ChromeResponse接口

2022-02-08 13:39:35

LinuxUNIX系统

2018-06-08 10:12:10

Web缓存体系服务器

2019-12-04 12:33:48

程序员技术设计

2021-05-27 21:18:56

谷歌Fuchsia OS操作系统

2021-04-28 11:35:06

Java框架日志

2021-05-28 06:16:28

蓝牙Wi-FiNFC

2020-12-01 10:18:16

RabbitMQ

2024-02-23 09:36:57

C#工具并行处理

2017-03-21 13:53:17

运维戴尔企业级解决方案

2020-03-30 09:22:03

AI语音技术机器视觉

2024-09-18 10:08:37

2022-05-09 07:27:50

ThreadLocaJava

2019-11-27 10:54:43

Tomcat连接数线程池
点赞
收藏

51CTO技术栈公众号