定义生成元素向量C ++的函数时正确的方法是啥
Posted
技术标签:
【中文标题】定义生成元素向量C ++的函数时正确的方法是啥【英文标题】:What is the correct approach while defining a function that generates a vector of elements C++定义生成元素向量C ++的函数时正确的方法是什么 【发布时间】:2014-04-14 23:34:41 【问题描述】:我必须在 C++ 中定义一个函数来生成类 Foo 的向量并返回它。
我可以用不同的方式定义它,它们都是相当的:
C风格参数传递:
void generateFooVector(vector<Foo> * result)
for (int i = 0; i < 100; i++)
Foo f = Foo();
result->push_back(f);
int main()
vector<Foo> result;
generateFooVector(&result);
参考传递传递:
void generateFooVector(vector<Foo> & result)
for (int i = 0; i < 100; i++)
Foo f = Foo();
result->push_back(f);
int main()
vector<Foo> result;
generateFooVector(result);
实例化向量并返回指针:
vector<Foo> * generateFooVector()
vector<Foo> * result = new vector<Foo>();
for (int i = 0; i < 100; i++)
Foo f = Foo();
result->push_back(f);
return result;
int main()
vector<Foo> * result = generateFooVector();
C 风格的传递和实例化 Foo 类:
void generateFooVector(vector<Foo *> * result)
for (int i = 0; i < 100; i++)
Foo * f = new Foo();
result->push_back(f);
int main()
vector<Foo *> result;
generateFooVector(&result);
引用传递 + Foo 实例:
void generateFooVector(vector<Foo *> & result)
for (int i = 0; i < 100; i++)
Foo * f = new Foo();
result->push_back(f);
int main()
vector<Foo *> result;
generateFooVector(result);
Vector 实例 + Foo 实例:
vector<Foo*> * generateFooVector()
vector<Foo*> * result = new vector<Foo*>();
for (int i = 0; i < 100; i++)
Foo * f = new Foo();
result->push_back(f);
return result;
int main()
vector<Foo *> * result = generateFooVector();
按值传递:
vector<Foo> generateFooVector()
vector<Foo> result;
for (int i = 0; i < 100; i++)
Foo f = Foo();
result->push_back(f);
return result;
int main()
vector<Foo > result = generateFooVector();
也许还有很多其他的解决方案。
所有当前的方式在范围内如何分配内存都有不同的含义。
也许我缺乏理论,但我真的很困惑如何确定每个用例的更好选择,以及为什么一个比另一个更好。你能帮帮我吗?
【问题讨论】:
注意:使用std::generate
。
how to "return an object" in C++ 的可能重复项
一百个push_backs?你听说过resize 和fill吗?
@Tacet,或reserve
和std::back_inserter
。如果您每次都需要调用该函数,std::fill
就没有用了,但这里可能就是这种情况。
@chris 我知道这很重要,但这只是一个例子。那不是重点。我在问如何返回元素集合。
【参考方案1】:
我确实看到了两个主要的不同实现 - vector<Foo*>
和 vector<Foo>
。其他都是参数传递的变种。
如您所知,vector 在添加时复制元素,在删除vector 时调用元素析构函数。因此,在vector<Foo*>
的情况下,您要避免复制对象,如果对象非常大,这些对象可能会增加。 但是你有责任删除记忆。所以最好存储带有自动删除功能的智能指针,比如vector<shared_ptr<Foo>>
或vector<unique_ptr<Foo>>
(这里需要移动逻辑)。指针的类型取决于您的应用程序逻辑。
顺便说一句,
Foo* pFoo = new Foo; // also default constructor call
(你知道the difference 和new Foo()
吗?)
vector<Foo> v(n); // init vector with n Foo objects calling default ctor
【讨论】:
【参考方案2】:该函数已在标准库中定义,称为std::vector<Foo>
的构造函数。您可以将其称为(C++11 风格)
auto v = std::vector<Foo>(n); // create n default constructed Foo() objects
或以 C++98 风格(在 C++11 中也有效)为
std::vector<Foo> v(n);
我不会尝试包装这个。
首先,您不想在其中隐藏像 100 这样的幻数,而不是在参数列表(模板或运行时)中隐藏。 不包装构造函数的另一个原因是为了避免读者混淆。如果我遇到generateVector
函数,第一个问题是:“与普通的构造函数调用相比,这个函数是否做了其他/额外的事情?”
【讨论】:
好的,但是auto
的意义何在?实例化对象的“正常”方式有什么好处吗?
他为 C++11 推荐的 Sutter's AAA(几乎总是自动)风格。一个优点是所有变量声明/定义都具有相同的从左到右的形式。
是的,我知道 AAA。但在这种情况下,它看起来像是一种反模式。
@juanchopanza 我认为反模式太强了,因为没有效率损失或任何东西,而只是可读性问题。无论如何,我更新以反映旧样式在 C++11 中仍然有效。
我想这需要一些时间来适应,或者会被丢弃。但它确实对类型提出了额外的要求:它必须是可复制的或可移动复制的。【参考方案3】:
我认为这在很大程度上取决于实际使用情况——最关键的是 foo 有多重? 如果 foo 是轻量级对象和类似 POD 的类型,那么我会使用
Vector<Foo> generateFoo();
否则可能是这样的:
Vector<Foo*> generateFoo();
可能更有用。
标准类型是高效的,并且最喜欢被复制,因此它们对 vector* 解决方案的优势不大。 一般来说,返回(或使用)需要稍后删除的原始指针可能是不明智的——在这种架构中很容易产生泄漏。所以也许看看你的 foo 对象的各种智能指针选项。
【讨论】:
这种指针/智能指针方法无缘无故地让事情变得过于复杂。 如果 foo 是一个大的或昂贵的对象(比如一个打开的文件缓冲区),那么你必须使用某种指针(因为复制是不切实际的)。 不,因为副本很可能会被忽略。所以你应该只玩复杂的指针技巧如果你的编译器/平台不能执行复制省略和如果你没有便宜的移动构造函数。【参考方案4】:我可能会采用上面采用的参考传递方法。
通常在 C 和 C++ 中,您不希望返回需要稍后释放的对象,因为这可能会导致泄漏。
【讨论】:
【参考方案5】:做事
vector<Foo> generateFooVector()
return vector<Foo>(100);
它叫modern c++
。
【讨论】:
有史以来最佳答案长度与问题长度之比。 它甚至不是现代的。复制省略一直是标准,编译器从 1962 年就开始支持它。 由于移动语义,这在 C++11 中可能没问题,但在 C++99 中可能会出现问题,因为需要在返回时进行复制。 hm,幻数 100 不是接口的一部分,应该是运行时参数 @doron 否,除非您的编译器/平台不支持 RVO。以上是关于定义生成元素向量C ++的函数时正确的方法是啥的主要内容,如果未能解决你的问题,请参考以下文章