最初,我有一个这样的问题:我有一个包含数据的向量,并且想要执行n次操作。就地完成它是不可能的,所以在每个循环周期中都会构造一个新的向量,操作完成并释放内存。对于我的问题,什么样的操作并不重要,但从数学上讲,它是一个排列的平方(我只想想出一个不能就地完成的操作)。它将result[i] = in[in[i]]应用于所有元素。
vector<int> *sqarePermutationNTimesUnsafe(vector<int> *in, int n)
{
if (n <= 0) { throw; }
vector<int> *result = in;
vector<int> *shuffled;
for (int i = 0; i < n; i++)
{
shuffled = new vector<int>(in->size(), 0);
for (int j = 0; j < in->size(); j++)
{
(*shuffled)[j] = (*result)[(*result)[j]];
}
if (result != in) { delete result; }
result = shuffled;
}
return result;
}提高速度的主要原因是,新数据被写入shuffled,然后只需要交换指针就可以进行下一步的混洗。它可以工作,但它很难看,而且容易出错。所以我想一个更好的方法是使用现代的c++。传递引用应该比传递vectors<...>更快,所以我这样做了。由于不能直接交换引用,因此我将其拆分为两个函数,并依靠返回值优化来交换引用。
// do the permutation
vector<int> sqarePermutation(const vector<int> &in)
{
vector<int> result(in.size(), 0);
for (int i = 0; i < in.size(); i++)
{
result[i] = in[in[i]];
}
return result;
}
// do the permutation n times
vector<int> sqarePermutationNTimes(const vector<int> &in, const int n)
{
vector<int> result(in); // Copying here is ok and required
for (int i = 0; i < n; i++)
{
result = sqarePermutation(result); // Return value optimization should be used so "swap" the references
}
return result;
}我想确保RVO正常工作,所以我写了一个小程序来测试它。
#include <iostream>
using namespace std;
class RegisteredObject
{
private:
int index;
public:
RegisteredObject(int index);
~RegisteredObject();
int getIndex() const;
};
RegisteredObject::RegisteredObject(int index): index(index)
{
cout << "- Register Object " << index << endl;
}
RegisteredObject::~RegisteredObject()
{
cout << "- Deregister Object " << index << endl;
}
int RegisteredObject::getIndex() const
{
return index;
}
RegisteredObject objectWithIncreasedIndex(const RegisteredObject &object)
{
return RegisteredObject(object.getIndex() + 1);
}
int main() {
cout << "Init a(0)" << endl;
RegisteredObject a(0);
cout << "RVO from a to a" << endl;
a = objectWithIncreasedIndex(a); // Seems to be buggy
cout << "RVO from a to b" << endl;
RegisteredObject b = objectWithIncreasedIndex(a); // Why does a get destructed here?
cout << "End" << endl;
return 0;
}不要犹豫在你的机器上尝试它,结果可能会有所不同。该程序有一个简单的数据对象,它显示构造函数和析构函数何时被调用。我使用的是针对x86_64-apple-darwin19.6.0的MacOSClang11.0.3。它会产生以下结果:
Init a(0)
- Register Object 0
RVO from a to a
- Register Object 1
- Deregister Object 1 //EDIT: <--- this should be Object 0
RVO from a to b
- Register Object 2
End
- Deregister Object 2
- Deregister Object 1如你所见,对象1永远不会被析构。我认为这是因为RVO造成的。RVO将新对象1构造到对象0的位置。但是因为忘记了创建对象0的临时副本,所以析构函数get使用索引1来调用。
将索引声明为const有助于防止此错误,因为编译器会抛出错误。
object of type 'RegisteredObject' cannot be assigned because its copy
assignment operator is implicitly deleted但我不认为这是解决方案。对我来说,似乎C++的RVO (或者至少clang的) RVO被破坏了,这意味着上面的置换实现可能会尝试双倍释放内存,或者根本不使用RVO。
那么首先,你认为是什么原因导致Bug没有释放Object 1?
如何实现一个高效而漂亮的sqarePermutationNTimes方法呢?
发布于 2020-09-21 07:10:27
在监控构造函数/析构函数时,不要忘记复制(/move)构造函数和赋值,然后你会得到类似于:
Init a(0)
- Register Object {0x7ffc74c7ae08: 0}
RVO from a to a
- Register Object {0x7ffc74c7ae0c: 1}
assign {0x7ffc74c7ae08: 0} <- {0x7ffc74c7ae0c: 1}
- Deregister Object {0x7ffc74c7ae0c: 1}
RVO from a to b
- Register Object {0x7ffc74c7ae0c: 2}
End
- Deregister Object {0x7ffc74c7ae0c: 2}
- Deregister Object {0x7ffc74c7ae08: 1}Demo
应用RVO是因为您没有复制构造函数调用。
但是赋值仍然存在。
a = objectWithIncreasedIndex(a);等同于
a = RegisteredObject(a.getIndex() + 1);而不是
a = RegisteredObject(RegisteredObject(a.getIndex() + 1));多亏了RVO。
对于您的第一个代码片段,您已经创建了so n (move-)assignments和n temporaries。
您可以通过使用(旧的)输出变量的方式来减少临时变量。
// do the permutation
void squarePermutation(const std::vector<int> &in, std::vector<int>& result)
{
result.resize(in.size());
for (int i = 0; i != in.size(); ++i) {
result[i] = in[in[i]];
}
}
// Convenient call
std::vector<int> squarePermutation(const std::vector<int> &in)
{
std::vector<int> result;
squarePermutation(in, result);
return result;
}
// do the permutation n times
vector<int> sqarePermutationNTimes(const vector<int> &in, const int n)
{
std::vector<int> result(in); // Copying here is ok and required
std::vector<int> tmp; // outside of loop so build only once
for (int i = 0; i != n; i++)
{
squarePermutation(result, tmp);
std::swap(result, tmp); // Modify internal pointers
}
return result;
}https://stackoverflow.com/questions/63983995
复制相似问题