我有以下情况:
T* get_somthing(){
std::vector<T> vec; //T is trivally-copyable
//fill vec
T* temp = new T[vec.size()];
memcpy(temp, vec.data(), vec.size() * sizeof(T));
return temp;
}
我希望通过直接返回std::vector::data
来摆脱复制过程:
T* get_somthing(){
std::vector<T> vec; //T is trivally-copyable
//fill vec
return temp.data();
}
但是,这是错误的,因为在调用vec
析构函数时将删除数据。
那么,如何防止vec删除其数据呢?换句话说,我想要从std::vector
到C ++ Raw Dynamic Array的某种move-idiiom。
P.S。改变设计不是一种选择。使用std::vector
是强制性的。将pointer
返回array
也是强制性的。 Becauese它是两个模块之间的包装。一个需要矢量另一个需要指针。
答案 0 :(得分:19)
P.S。改变设计不是一种选择。使用std :: vector是强制性的。返回指向数组的指针也是必需的。
更改设计是您的最佳选择。我建议重新考虑这个立场。
(目前†)没有办法偷走&#34;窃取&#34;一个向量的缓冲区,所以考虑到问题中所述的(愚蠢的††)限制,复制是要走的路。
†Tomasz Lewowski联系了一项提案,如果它包含在未来的标准中,将会改变这一提议:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4359.pdf(编辑:如指出,它被c ++ 17拒绝)
††愚蠢直到具体要求为止。
它是两个模块之间的包装器。一个需要矢量另一个需要指针。
据推测,需要指针的另一个接口会将缓冲区的破坏委托给调用者,可能会使用void delete_somthing(T*)
之类的某种回调。在我看来,取得所有权而不给予回报将是非常糟糕的设计。
如果你确实控制了破坏,你可以将矢量存储在地图中,并在指针被传递以进行销毁时删除矢量:
std::unordered_map<T*, std::vector<T>> storage;
T* get_somthing(){
std::vector<T> vec; //T is trivally-copyable
//fill vec
T* ptr = vec.data();
storage[ptr] = std::move(vec);
return ptr;
}
void delete_somthing(T* ptr){
storage.erase(ptr);
}
答案 1 :(得分:14)
在C ++ 11中,没有选项可以从向量中释放缓冲区。
对C ++ 17提出了对标准的这种扩展:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4359.pdf但是,作为T.C.指出,它被拒绝了:https://issues.isocpp.org/show_bug.cgi?id=81
所以标准中没有运气。 此外,相关问题已经发布,已经回答并解释了同样的问题:Destroy std::vector without releasing memory
如果您能够使用其中任何一个库,您可以尝试自定义分配器或其他奇怪的东西(比如将自己绑定到库的内部实现并弄乱私有矢量数据),但实际上,不< / em>的。
答案 2 :(得分:6)
下面是如何使用自定义分配器执行此操作的示例代码。这假设您可以使vector
实际使用自定义分配器。此外,allocator使用静态变量来控制内部缓冲区的破坏。我已经在VS2015下进行了检查,并且它的实现调用仅在内部缓冲区的~vector中释放deallocate - 即它不管理使用此分配器的任何其他分配。
这是一个黑客 - 而且我不确定它的使用会带来什么后果。它确实不是线程安全的(但是在使allow_dealloc线程本地化之后可以很容易地修复)。:
http://coliru.stacked-crooked.com/a/5d969a6934d88064
#include <limits>
#include <vector>
#include <iostream>
template <class T>
class my_alloc {
std::allocator<T> alloc;
public:
static bool allow_dealloc;
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
pointer allocate(size_type num, const void* = 0) { return alloc.allocate(num); }
void deallocate(pointer p, size_type num) {
if (allow_dealloc)
alloc.deallocate(p, num*sizeof(T)); }
// Squashed as less important
template <class U> struct rebind { typedef my_alloc<U> other; };
pointer address(reference value) const { return &value; }
const_pointer address(const_reference value) const { return &value; }
my_alloc() throw() { }
my_alloc(const my_alloc&) throw() { }
template <class U> my_alloc(const my_alloc<U>&) throw() { }
~my_alloc() throw() { }
size_type max_size() const throw() { return (std::numeric_limits<size_t>::max)() / sizeof(T); }
void construct(pointer p, const T& value) { alloc.construct(p, value); }
void destroy(pointer p) { p->~T(); }
};
template <typename T>
bool my_alloc<T>::allow_dealloc = true;
int main()
{
int* data = 0;
size_t size = 0;
{
my_alloc<int>::allow_dealloc = true;
std::vector<int, my_alloc<int>> vec= { 0, 1, 2, 3 };
vec.push_back(4);
vec.push_back(5);
vec.push_back(6);
my_alloc<int>::allow_dealloc = false;
data = vec.data();
size = vec.size();
}
for (size_t n = 0; n < size; ++n)
std::cout << data[n] << "\n";
my_alloc<int> alloc;
alloc.deallocate(data, size);
}
答案 3 :(得分:3)
我不知道你是否会喜欢这个非常hacky的解决方案,我绝对不会在生产代码中使用它,但请考虑:
#include <iostream>
using namespace std;
#include <vector>
template<class T>
T* get_somthing(){
std::vector<T> vec = {1,2,3}; //T is trivally-copyable
static std::vector<T> static_vector = std::move(vec);
return static_vector.data();
}
int main() {
int * is = get_somthing<int>();
std::cout << is[0] << " " << is[1] << " " << is[2];
return 0;
}
所以,正如你在get_somthing
中看到的那样,我定义了一个与你需要的相同类型的静态向量,并在其上使用std::move
,并返回它data()
}。它实现了你想要的,但这是危险的代码,所以请使用好的老式复制数据方法,让我们等到N4359进入主流编译器。
答案 4 :(得分:3)
如果有选项可以使用智能指针,我建议std::shared_ptr
使用别名:
template<typename T>
std::shared_ptr<T> get_somthing(){
using Vector = std::vector<T>;
using ReturnT = std::shared_ptr<T>;
std::vector<T>* vec = new std::vector<T>;
//fill vec
std::shared_ptr<Vector> vectorPtr(vec); // (1)
std::shared_ptr<T> aliasedPtr(vectorPtr, vec->data()); // (2)
return aliasedPtr;
}
(1)将创建一个指向要别名化的向量的共享指针 (2)创建一个共享指针,它将销毁别名shared_ptr而不是删除包含的数据
答案 5 :(得分:2)
编辑:这个想法不起作用,因为没有办法阻止对基类析构函数的隐式调用(谢谢,molbdnilo)。如果我认为它们被称为是一件好事。
<小时/> 我不完全确定这是否可行(并且对其他人所说的很好奇),但是可以继承vector并覆盖它的析构函数(什么也不做)?即使~vector()
不是虚拟的(标准中是否有要求是虚拟的?),只要您明确使用您的类型,这应该有效。
通过继承你将保留所有的好处,特别是内存管理 - 除了最后一点(你不想要的)。
答案 6 :(得分:2)
Isn't the way to go here simply to allocate the vector dynamically? That's how resources with a life time which is unrelated to scope are traditionally managed, and I don't see any reason to invent something extraordinary.
Of course that vector should be destroyed some time later; that may make it necessary to store its address somewhere as a side effect of get_somthing()
, but even then this strategy seems cleaner than any of the other ideas.
答案 7 :(得分:2)
你要做的第一件事就是起床,去负责这个设计的人,(口头上以专业的方式)打他/她的脸:这是一个一团糟。< / p>
然后,在C ++ 11中有一种方法可以让std::vector
具有自动存储持续时间并且不会调用它的析构函数:
std::vector
放入union
像这样:
template<typename T>
union Ugly {
std::vector<T> vec;
Ugly() {
new (&vec) std::vector<T>(); // Construct
}
~Ugly() {
// Don't destruct
}
};
T* get_something(){
Ugly mess;
//fill mess.vec
return mess.vec.data();
}
我不是100%确定这是否仍然算作有效的C ++ 11,但它应该“有效”。现在请原谅,我需要洗手以摆脱这段代码的羞耻感......
哦,还有一件事:你打算如何释放std::vector
分配的内存?你知道,你不能(可靠地)使用成员函数data()
返回的指针!
答案 8 :(得分:2)
好的,不要在家里尝试这个,这不好看。它更像是一个实验,而不是一个真正的答案。
(嗯,要求/设计也不好:&#34;玩愚蠢的游戏,赢得愚蠢的奖品&#34;)
在你的cpp:
#define private public // good luck for the code review
#define protected public
#include <vector> // (must be the first occurence in the TU)
#undef private // do not abuse good things...
#undef protected
template<typename T>
T* my_release(std::vector<T>& v){
std::vector<T> x; // x: the local vector with which we mess around
std::swap(x, v); // the given vector is in an OK, empty state now.
T* out = x._M_impl._M_start; // first, get the pointer you want
// x will be destructed at the next '}'.
// The dtr only use _M_start and _M_finish, make sure it won't do anything.
x._M_impl._M_start = nullptr;
x._M_impl._M_finish = nullptr;
// no need to say, the internal state of 'x' is bad, like really bad...
// also we loose the capacity information, the actual allocator...
// -> good luck with memory leaks...
return out;
}
// usage example
int main(){
std::vector<int> vi{1,2,3,4,5,6,7,8,9};
auto n = vi.size();
int* pi = release(vi);
for(size_t i=0; i<n; ++i)
std::cout << pi[i] << ", ";
return 0;
}
打印1, 2, 3, 4, 5, 6, 7, 8, 9,