class 中的所有 objects 都应该是参考吗?

Should all objects in a class be a reference?

总是将我的 object 成员的数据类型作为引用是一种不好的做法吗?

阅读这篇文章后 question about avoiding #include 我一直在努力避免在我当前的 C++ 项目中使用 #include 并始终向前声明我的数据类型。

这是我项目中的示例代码片段。在 header 我有 EnemyBuilder &m_builder;然后我在源代码中初始化它。

EnemyFactory.h

#include <string>

#ifndef ENEMYFACTORY
#define ENEMYFACTORY

class World;
class Enemy;
class EnemyBuilder;

class EnemyFactory
{
private:
    EnemyBuilder &m_builder;
    World &m_world;

public:
    EnemyFactory(World &world);

    Enemy* produce(std::string type);
};

#endif

EnemyFactory.cpp

#include "EnemyFactory.h"

#include "EnemyBuilder.h"
#include "World.h"
#include "Enemy.h"

EnemyFactory::EnemyFactory(World &world) :
    m_world(world), m_builder(EnemyBuilder())
{

}

Enemy* EnemyFactory::produce(std::string type)
{
    Enemy *product = new Enemy(m_world);

    if (type == "basic")
    {
        product->setSpeed(5000.0f);
    }

    return product;
}

我觉得你是在用锤子打蚂蚁。前向声明是一种工具,但并不总是一种"best practice"。您必须考虑将来有人(或您)阅读您的代码,他们必须了解您的代码在做什么。

我建议你这个方法:

EnemyFactory.h

 #include <string>

#ifndef ENEMYFACTORY
#define ENEMYFACTORY

class EnemyFactory
{
private:
    EnemyBuilder m_builder;
    World m_world;

public:
   EnemyFactory(World &world);

   Enemy* produce(std::string type);
};

#endif

EnemyFactory.cpp

#include "EnemyBuilder.h"
#include "World.h"
#include "Enemy.h"

#include "EnemyFactory.h"// this include must be last one
EnemyFactory::EnemyFactory(World &world)
{

}

Enemy* EnemyFactory::produce(std::string type)
{
    Enemy *product = new Enemy(m_world);

    if (type == "basic")
    {
        product->setSpeed(5000.0f);
    }

    return product;
}

包含顺序略有变化,您可以省略前向声明和引用

请先查看 ...在您当前的代码中,您正在为自己堆积 桶火药,您可能有一天会点燃它.... ...很快有一天... 补救。

In EnemyFactory.h, you have a line... Prefer to use std::unique_ptr

 Enemy* produce(std::string type);

So that, this becomes

std::unique_ptr<Enemy> produce(std::string type);

再次 EnemyFactory.cpp...

You have the dreaded Undefined Behaviour, basically, your program will crash because you binded a reference to a temporary object

EnemyFactory::EnemyFactory(World &world) :
    m_world(world), m_builder(EnemyBuilder())  //<---This will crash
{

}

您尝试做的更大范围适用于 PIMPL 习语。

最好的方法是

EnemyFactory.h

#include <string>
#include <memory>

#ifndef ENEMYFACTORY
#define ENEMYFACTORY

class World;
class Enemy;
class EnemyBuilder;

class EnemyFactory
{
private:
    std::unique_ptr<EnemyBuilder> m_builder;
    std::shared_ptr<World> m_world;  //<- I am guessing World is a shared object based on your constructor

public:
    EnemyFactory(World &world);

    std::unique_ptr<Enemy> produce(std::string type);
};

#endif

在EnempyFactory.cpp

#include "EnemyFactory.h"

#include "EnemyBuilder.h"
#include "World.h"
#include "Enemy.h"

//I hope it is guaranteed the instance if World, world will always outlive any instance of EnemyFactory...

EnemyFactory::EnemyFactory(World &world) :
    m_world(&world), m_builder(std::make_unique<EnemyBuilder>())
{

}

std::unique_ptr<Enemy> EnemyFactory::produce(std::string type)
{
    std::unique_ptr<Enemy> product = std::make_unique<Enemy>(m_world);

    if (type == "basic")
    {
        product->setSpeed(5000.0f);
    }

    return product;
}

我们这里没有使用引用,因为所有 class 引用成员都必须在构造函数中实例化,管理生命周期可能会很麻烦,不过取决于您的用例....

指针在这里更方便....记住,每当您想到指针时,请先选择 std::unique_ptrs,然后在共享对象时,您可以选择 'std::shared_ptr`。

Is it bad practice to always make my object member's data type a reference?

这不仅是不好的做法,而且效率低且限制性强。

Why?

  1. 包含引用的 classes 的对象自动不可分配。当将引用类型绑定到函数对象时,标准有 std::reference_wrapper<>(基本上是一个包装指针)来绕过这个限制。

  2. 您有义务管理对象的生命周期。由于您存储的是引用而不是对象,因此您的 class 无法自行清理。此外,它不能确保它所引用的对象仍然存在。你刚刚失去了 c++ 给你的所有保护。欢迎来到石器时代。

  3. 效率较低。现在,所有对象访问都通过本质上是指针的方式取消引用。而且您很可能会丢失缓存位置。毫无意义。

  4. 你失去了将对象视为值的能力。这将导致程序效率低下、复杂。

  5. 我可以继续...

What should I do?

更喜欢值语义(将对象视为值)。让它们被复制(如果你的 classes 写得很好,大部分副本都会被删除)。将它们封装并组合成更大的对象。让容器管理组件的生命周期。这是它的工作。

What about the time cost of including a few header files?

除非您在成百(上千)个模板 class 中进行极其复杂的模板扩展,否则它甚至不太可能成为问题。泡咖啡浪费的时间比编译几个源文件的时间还多。