我们的老师正在教我们一些优化技巧,为此她给了我们以下 C++ 代码来优化。
struct IStruct
{
virtual bool GetType() const = 0;
};
struct FStructA : public IStruct
{
std::string Key;
int IntData;
virtual bool GetType() const override
{
return true;
}
};
struct FStructB : public IStruct
{
std::string Key;
float FloatData;
virtual bool GetType() const override
{
return false;
}
};
class SomeSystem
{
public:
void Add(const FStructA& Struct)
{
//DataElements.push_back(std::shared_ptr<IStruct>(new FStructA(Struct)));
DataElements.emplace_back(std::shared_ptr<IStruct>(new FStructA(Struct)));
}
void Add(const FStructB& Struct)
{
//DataElements.push_back(std::shared_ptr<IStruct>(new FStructB(Struct)));
DataElements.emplace_back(std::shared_ptr<IStruct>(new FStructB(Struct)));
}
void Process()
{
for (std::shared_ptr<IStruct> DataElement : DataElements)
{
if (DataElement->GetType())
{
FStructA StructA = *static_cast<FStructA*>(DataElement.Get());
// Process A
}
else
{
FStructB StructB = *static_cast<FStructB*>(DataElement.Get());
// Process B
}
}
}
private:
std::vector<std::shared_ptr<IStruct>> DataElements;
};
现在,如您所见,我已经优化了一行,而不是push_back
使用emplace_back
. 这肯定会节省一些内存并提高速度,但不会那么多。我认为这不是这个问题的答案。肯定还有其他我想念的东西。
另一点是重载的Add
功能,我想优化它,但我认为它正确地完成了它的工作。
因此我很困惑,我还能在哪里优化?
FStructA
并FStructB
共享一个公共成员Key
,因此可以考虑将其移动到它们都派生自的基类。
在处理多态类型时,基类几乎总是需要一个virtual
析构函数,以便在通过基类指针销毁对象时正确调用派生构造函数。这在使用智能指针时尤其重要,例如在多态对象的容器中,正如这段代码所做的那样。
struct
成员是public
默认的,并且默认情况下使用继承struct
从另一个派生,因此显式声明和继承自是多余的。struct
public
FStructA
FStructB
public
IStruct
使用时override
,无需virtual
再次指定。override
暗示virtual
其本质。
避免new
与智能指针构造函数一起使用。改用std::make_...()
函数 - std::make_unique()
forstd::unique_ptr
和std::make_shared()
for std::shared_ptr()
。
重载的Add()
方法可以合并为一个模板方法。
emplace_back()
与push_back()
处理(智能)指针时相同,尤其是处理必须在容器外部构造的多态类型时。仅当容器本身能够直接构造元素时,使用emplace_back()
而不是push_back()
有意义,然后您只需提供emplace_back()
构造函数参数。但在这段代码中情况并非如此。
在您的range-for
循环中,您正在DataElement
按 value 获取,这将制作每个shared_ptr
in的副本DataElements
,从而在它们进入和离开作用域时递增和递减它们的引用计数。虽然这很好,但这是不必要的开销。你应该DataElement
参考参考。事实上,在迭代任何容器时,您通常应该使用引用,除非您确实需要副本,或者制作副本的开销可以忽略不计(即,使用平凡类型)。
auto
在声明变量时,尤其是在处理模板类型时,请尽可能考虑使用。这在range-for
循环中特别有用。
不要static_cast
与 a 的原始指针一起shared_ptr
使用,static_pointer_cast
而是使用来维护正确的共享所有权语义。
不要不必要地复制对象。您正在取消引用 的结果static_cast
,然后制作对象的副本,然后您正在处理副本,而不是原始对象。这有时很有用,但此代码不是其中之一。使用指针或引用来避免复制。
除非确实需要,否则不要将基类强制转换为派生类。如果您有多态类型(如此代码所做的那样),请尽可能使用多态行为(即虚拟方法)。正确的多态处理逻辑不应该关心它在操作什么类型。
不要使用std::shared_ptr
什么时候std::unique_ptr
就足够了。std::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();
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句