使用 std::set 和 shared_ptr 优化 C++ 代码

Optimizing C++ code with std::set and shared_ptr

我们的老师正在教我们一些优化技术,为此她给了我们以下 C++ 代码来优化。

另一点是重载的Add函数,我想优化它,但我认为它正确地完成了它的工作。

所以我很困惑,还有什么地方可以优化?

FStructAFStructB 共享一个共同的成员 Key,因此可以考虑将其移动到它们都派生自的基础 class。

在处理多态类型时,基本 class 几乎总是需要一个 virtual 析构函数,以便在通过基本 class 指针销毁对象时正确调用派生构造函数。这在使用智能指针时尤其重要,例如在多态对象的容器中,就像这段代码所做的那样。

struct 成员默认为 publicstruct 派生自另一个 struct,默认使用 public 继承,因此明确说明 FStructAFStructBIStruct 继承 public 是多余的。

使用override时,不需要再指定virtualoverride 就其本质而言意味着 virtual

避免将 new 与智能指针构造函数一起使用。使用 std::make_...() 函数 - std::make_unique() 用于 std::unique_ptrstd::make_shared() 用于 std::shared_ptr().

重载的 Add() 方法可以合并到一个模板方法中。

emplace_back() 在处理(智能)指针时与 push_back() 相同,尤其是对于必须在容器外部构造的多态类型。只有当容器本身能够直接构造元素时,使用 emplace_back() 而不是 push_back() 才有意义,然后您只需提供 emplace_back() 构造函数参数。但在这段代码中情况并非如此。

在你的 range-for 循环中,你正在 DataElement 按值 ,这将在 [=] 中复制每个 shared_ptr 43=],因此在它们进入和离开作用域时递增和递减它们的引用计数。虽然这很好,但这是不必要的开销。您应该改为参考 DataElement 。事实上,在迭代任何容器时,您通常应该使用引用,除非您确实需要副本,或者制作副本的开销可以忽略不计(即,使用普通类型)。

尽可能考虑在声明变量时使用 auto,尤其是在处理模板类型时。这在 range-for 循环中特别有用。

不要将 static_castshared_ptr 的原始指针一起使用,而是使用 static_pointer_cast 以维护适当的共享所有权语义。

不要不必要地复制对象。您取消引用 static_cast 的结果,然后制作对象的副本,然后处理副本,而不是原始对象。这有时很有用,但这段代码不是其中之一。使用指针或引用来避免复制。

除非确实需要,否则不要将基础 class 转换为派生 class。如果您有多态类型(正如这段代码所做的那样),请尽可能使用多态行为(即虚方法)。正确的多态处理逻辑不应该关心它在什么类型上运行。

std::unique_ptr 就足够时,不要使用 std::shared_ptrstd::shared_ptr 具有 std::unique_ptr 没有的开销(引用计数、控制块等)。共享所有权有其用途,但此代码并未展示它们。

话虽如此,试试这样的东西:

struct IStruct
{
    std::string Key;
    virtual ~IStruct() = default;
    //virtual bool GetType() const = 0;
    virtual void Process() = 0;
};

struct FStructA : IStruct
{
    int IntData;

    /*
    bool GetType() const override
    {
        return true;
    }
    */

    void Process() override
    {
        // Process A
    }
};

struct FStructB : IStruct
{
    float FloatData;

    /*
    bool GetType() const override
    {
        return false;
    }
    */

    void Process() override
    {
        // Process B
    }
};

class SomeSystem
{
public:
    template<typename T, typename... Args>
    void Add(Args&&... args)
    {
        DataElements.push_back(/*std::make_shared*/std::make_unique<T>(std::forward<Args>(args)...));
    }

    void Process()
    {
        for (auto &DataElement : DataElements)
        {
            /*
            if (DataElement->GetType())
            {
                auto StructA = static_pointer_cast<FStructA>(DataElement);
                // Process A
            }
            else
            {
                auto StructB = static_pointer_cast<FStructB>(DataElement);
                // Process B
            }
            */

            DataElement->Process();
        }
    }

private:
    std::vector</*std::shared_ptr*/std::unique_ptr<IStruct>> DataElements;
};

int main()
{
    SomeSystem system;
    system.Add<FStructA>(...params as needed...);
    system.Add<FStructB>(...params as needed...);
    system.Process();
}