拒绝std::vector删除其数据
Deny std::vector from deleting its data
我有以下情况:
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++原始动态数组的移动。
附言:改变设计不是一种选择。使用std::vector
是强制性的。将pointer
返回到array
也是强制性的。Becauese它是两个模块之间的包装器。一个需要矢量,另一个需要指针。
附言:改变设计不是一种选择。使用std::vector是强制性的。返回指向数组的指针也是必需的。
更改设计是您的最佳选择。我建议重新考虑这一立场。
(目前†)没有办法"窃取"向量的缓冲区,因此考虑到问题中所述的(愚蠢的x20†)限制,复制是可行的。
†Tomasz Leowski将一项提案联系起来,如果将其纳入未来标准,该提案将改变这一点: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);
}
在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
所以在标准上没有运气。此外,还发布了一个相关问题,该问题已经得到回答,并解释了相同的问题:在不释放内存的情况下销毁std::vector
如果你能够处理这两个库中的任何一个,你可以尝试自定义分配器或其他奇怪的东西(比如将自己绑定到库的内部实现并处理私有向量数据),但实际上,不要。
下面是如何使用自定义分配器的示例代码。这假设您可以使vector
实际使用自定义分配器。分配器还使用静态变量来控制内部缓冲区的破坏。我已经在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);
}
如果有使用智能指针的选项,我建议使用别名为的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,而不是删除包含的数据
我不知道你是否会喜欢这个非常黑客的解决方案,我肯定不会在生产代码中使用它,但请考虑:
#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进入主流编译器。
现场演示:http://ideone.com/3XaSME
编辑:这个想法不起作用,因为没有办法阻止对基类的析构函数的隐式调用(谢谢,molbdnilo)。如果我想起来的话,他们被称为是一件好事。
我不完全确定这是否可行(也很好奇其他人怎么说),但是否有可能从向量继承并覆盖其析构函数(什么都不做)?即使
~vector()
不是虚拟的(标准中是否有虚拟或非虚拟的要求?)只要您明确使用您的类型,这也应该有效。
通过继承,您将保留所有的好处,特别是内存管理——除了最后一位(您不想要)。
这里的方法不是简单地动态分配向量吗?这就是生命周期与范围无关的资源传统上的管理方式,我认为没有任何理由发明一些非凡的东西。
当然,这个载体应该在一段时间后被销毁;这可能需要将其地址存储在某个地方,作为get_somthing()
的副作用,但即使这样,这种策略似乎也比任何其他想法都干净。
你应该做的第一件事就是站起来,去找负责这个设计的人,(以专业的方式口头)打他/她的脸:这是一场混乱。
然后,在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()
返回的指针!
好吧,不要在家里尝试,这不好。与其说是实际答案,不如说是一个实验。
(嗯,要求/设计也不好:"玩愚蠢的游戏,赢得愚蠢的奖品")
在你的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,
- 从 Typedef 数据类型中删除常量
- 从输入中删除重复项,而不使用任何数据结构
- 删除使数据处于"错误状态"的默认构造函数的模式?
- 从自定义数据类型向量中删除重复元素
- 如何使用C++删除/更新txt文件中的特定数据
- 如何使用C++删除/更新txt文件中的特定数据?
- 从文本文件中读取数据并删除所有换行符空格,并在 C++ 控制台中显示
- C++ - 按自定义数据类型向量的值删除元素
- 如何从 txt 文件中删除数据/内容
- 如何通过其持有的数据从链表中删除某个节点?
- 基于给定字符串数据类型的链表删除节点
- 如何在 C++ 中使用删除运算符删除单个数据
- 如何在每次循环迭代期间生成向量,存储数据,然后删除该向量?
- 如何从C ++的输出中删除垃圾数据(垃圾数据)
- 如何从 HTTP 响应中删除垃圾数据
- C++ 类析构函数删除部分但不是全部成员数据
- 在向量上调用 clear() 实际上不会删除 data() 中的数据?
- SOCAT,将TCP套接字转发到Unix套接字,将recvmsg()更改为read(),删除辅助数据
- 如何删除字符串中的所有空格并将数据存储在两者之间
- 筛选器重复列表-列出重复数据删除