有没有人曾经使用过C++的"贴牌新品"?如果是这样,那该怎么办?在我看来,它只对内存映射硬件有用.
每次有人delete[]在这里问一个问题时,总会有一个非常笼统delete[]的回答:“ C++ 就是这样做的,使用”。来自普通的 C 背景,我不明白为什么需要一个不同的调用。
使用malloc()/free()您的选择是获取指向连续内存块的指针并释放连续内存块。实现领域中的某些东西会根据基地址知道您分配的块的大小,以便您何时必须释放它。
没有功能free_array()。我在与此相关的其他问题上看到了一些疯狂的理论,例如调用delete ptr只会释放数组的顶部,而不是整个数组。或者更正确的是,它不是由实现定义的。当然……如果这是 C++ 的第一个版本,并且您做出了一个有意义的奇怪设计选择。但是为什么 with$PRESENT_YEAR的 C++ 标准没有被重载???
似乎 C++ 添加的唯一额外的一点是遍历数组并调用析构函数,我认为这可能是它的症结所在,它实际上是使用一个单独的函数来为我们节省单个运行时长度查找,或者nullptr在列表的末尾,以换取折磨每个新的 C++ 程序员或程序员,他们有一个模糊的一天并且忘记了有一个不同的保留字。
如果除了“这就是标准所说的并且没有人质疑它”之外还有其他原因,有人可以一劳永逸地澄清吗?
[expr.new]C++的5.3.4 2月草案给出了一个例子:
new(2,f) T[5]导致打电话operator new[](sizeof(T)*5+y,2,f).这里,x和y是非负的未指定值,表示数组分配开销; new-expression的结果将从返回的值中抵消此数量
operator new[].这种开销可以应用于所有数组新表达式,包括那些引用库函数operator new[](std::size_t, void*)和其他放置分配函数的表达式.开销的数量可能因新的一次调用而异.- 末端的例子 ]
现在来看以下示例代码:
void* buffer = malloc(sizeof(std::string) * 10);
std::string* p = ::new (buffer) std::string[10];
Run Code Online (Sandbox Code Playgroud)
根据上面的引用,第二行将new (buffer) std::string[10]在内部调用operator new[](sizeof(std::string) * 10 + y, buffer)(在构造单个std::string对象之前).问题是如果y > 0,预分配的缓冲区太小了!
那么我如何知道在使用数组放置时预先分配多少内存?
void* buffer = malloc(sizeof(std::string) * 10 + how_much_additional_space);
std::string* p = ::new (buffer) std::string[10];
Run Code Online (Sandbox Code Playgroud)
或者标准某处是否保证y == 0在这种情况下?报价再次说:
这种开销可以应用于所有数组新表达式,包括那些引用库函数
operator …
我一直试图弄清楚如何从C++ 17访问映射缓冲区而不调用未定义的行为.对于这个例子,我将使用Vulkan的返回缓冲区vkMapMemory.
因此,根据N4659(最终的C++ 17工作草案),[intro.object]部分(重点补充):
C++程序中的构造创建,销毁,引用,访问和操作对象.一个目的是通过一种创建定义(6.1),通过一个 新的表达式 (8.3.4)中,当隐式地改变所述一个联合的活性部件(12.3),或当一个临时对象被创建(7.4,15.2).
显然,这些是创建C++对象的唯一有效方法.因此,假设我们得到一个void*指向主机可见(和相干)设备内存的映射区域的指针(当然,假设所有必需的参数都有有效值并且调用成功,并且返回的内存块足够大)并正确对齐):
void* ptr{};
vkMapMemory(device, memory, offset, size, flags, &ptr);
assert(ptr != nullptr);
Run Code Online (Sandbox Code Playgroud)
现在,我希望将此内存作为float数组访问.显而易见的事情static_cast是指针并按照我的快乐方式继续如下:
volatile float* float_array = static_cast<volatile float*>(ptr);
Run Code Online (Sandbox Code Playgroud)
(volatile包含它因为它被映射为相干存储器,因此可以在任何时候由GPU写入).然而,在该存储器位置中技术上float不存在阵列,至少不是在引用的摘录的意义上,因此通过这样的指针访问存储器将是未定义的行为.因此,根据我的理解,我有两种选择:
memcpy数据它应该总是能够使用本地缓存,将它转换为std::byte*和memcpy的表示到映射区域.GPU将按照着色器中的指示解释它(在这种情况下,作为32位数组float),从而解决了问题.但是,这需要额外的内存和额外的副本,所以我宁愿避免这种情况.
new阵列看来,[new.delete.placement]部分没有对如何获得放置地址施加任何限制(无论实现的指针安全性如何,它都不必是安全派生的指针).因此,可以通过放置创建有效的浮点数组new,如下所示:
volatile …Run Code Online (Sandbox Code Playgroud) 请参阅以下代码:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Run Code Online (Sandbox Code Playgroud)
我可以假设(void*)t == (void*)p吗?
当前的标准草案明确指出放置new[]可能有空间开销:
这种开销可以应用于所有数组新表达式,包括那些引用库函数operator new [](std :: size_t,void*)和其他放置分配函数的表达式.开销的数量可能因新的一次调用而异.
所以大概他们有一些想法,为什么编译器需要这个开销.它是什么?编译器可以将此开销用于任何有用的东西吗?
在我的理解中,要破坏这个数组,唯一的解决方案是在一个循环中调用析构函数(我是否正确?),因为没有放置delete[](顺便说一下,我们不应该有delete[]正确的方法来破坏数组,不仅仅是它的元素?).因此编译器不必知道数组长度.
我认为这个开销不能用于任何有用的东西,编译器不会使用它(所以这在实践中不是问题).我用这个简单的代码检查了编译器:
#include <stdio.h>
#include <new>
struct Foo {
~Foo() { }
};
int main() {
char buffer1[1024];
char buffer2[1024];
float *fl = new(buffer1) float[3];
Foo *foo = new(buffer2) Foo[3];
printf("overhead for float[]: %d\n", (int)(reinterpret_cast<char*>(fl) - buffer1));
printf("overhead for Foo[] : %d\n", (int)(reinterpret_cast<char*>(foo) - buffer2));
}
Run Code Online (Sandbox Code Playgroud)
GCC和clang根本不使用任何开销.但是,MSVC使用8个字节的Foo情况.为什么MSVC会使用这种开销?
这是一些背景,为什么我提出这个问题.
之前有关于此主题的问题:
据我所知,这些问题的道德是避免使用放置new[],并new在循环中使用放置.但是这个解决方案不会创建一个数组,但是使用彼此相邻的元素(不是数组)operator[]是未定义的行为.这些问题更多的是关于如何避免安置new[],但这个问题更多的是关于"为什么?".
我想修改一个数组分配:
float * a = new float[n] ;
Run Code Online (Sandbox Code Playgroud)
使用对齐的分配器。我倾向于尝试使用placement new 和posix_memalign(或新的c++11 等价物),但看到placement new with arrays 在数组分配方面存在问题,因为编译器可能需要为计数或其他元数据提供额外的存储空间。
我试过:
int main()
{
float * a = new alignas(16) float[3] ;
a[2] = 0.0 ;
return a[2] ;
}
Run Code Online (Sandbox Code Playgroud)
但编译器似乎表明 alignas 被忽略:
$ g++ -std=c++11 t.cc -Werror
t.cc: In function ‘int main()’:
t.cc:4:39: error: attribute ignored [-Werror=attributes]
float * a = new alignas(16) float[3] ;
^
t.cc:4:39: note: an attribute that appertains to a type-specifier is ignored
Run Code Online (Sandbox Code Playgroud)
看起来使用 alignas …
在这种情况下,问题场景是游戏,因此所有资源在开始时分配,然后迭代一个级别.
存储在向量中的对象是复杂类的实例,当然在加载时将它们实际复制到向量中是耗时的,但是关注度低.
但是如果我主要担心的是在运行时对类对象进行迭代的速度,那么我是否更好地将类对象本身存储在向量中,而不是像传统推荐的那样只指向类对象?
我不担心这个例子中的内存管理,只是迭代的速度.
考虑以下Java代码.
int N = 10;
Object obj[] = new Object[N];
for (int i = 0; i < N; i++) {
int capacity = 1000 * i;
obj[i] = new ArrayList(capacity);
}
Run Code Online (Sandbox Code Playgroud)
因为在Java中,所有对象都存在于Heap中,所以数组不包含对象本身,而是包含对象的引用.此外,数组本身也是一个对象,因此它存在于堆上.
什么是C++中的等价物,但保持堆栈中的数组和对象,以尽可能避免需要new和delete?
编辑:更改代码以使用自定义构造函数.
可能重复:
可以以便携方式使用数组的新位置吗?
我想分配对象T的数组并使用对象构造函数初始化对象.使用c ++很容易new:
T * pointerT = new T [arraySize];
Run Code Online (Sandbox Code Playgroud)
它将为所有arraySize对象调用T构造函数.但是,出于某种原因,我必须使用C memalign而不是新的.在这种情况下,我最终使用以下代码
T * pointerT = (T*) memalign(64,arraySize * sizeof(T));
new (pointerT) T();
Run Code Online (Sandbox Code Playgroud)
new (pointerT) T()只调用T构造函数一次.但是,我需要为所有对象调用T构造函数,而不仅仅是第一个对象.
我非常感谢你的帮助.
不要问我我想要做什么,这只是一个快速测试,它的唯一目的是看看新的放置是否有问题.
我发现了一个问题,或者我只是误解了一些问题.
#include <vector>
using namespace std;
#define WORKS
int main(int argc, char** argv) {
vector<int>* pp = (vector<int>*)malloc(sizeof(vector<int>)*20);
#ifdef WORKS
for(int i = 0; i < 20; ++i)
new (pp+i) vector<int>;
#else
new (pp) vector<int>[20];
#endif
for(int i = 0; i < 20; ++i)
pp[i].~vector<int>();
}
Run Code Online (Sandbox Code Playgroud)
当你删除"#define WORKS"时,它会给你访问冲突,比如
for(int i = 0; i < 20; ++i)
new (pp+i) vector<int>;
Run Code Online (Sandbox Code Playgroud)
哪作得好,不同于
new (pp) vector<int>[20];
Run Code Online (Sandbox Code Playgroud)
这是在破坏阶段抛出异常的原因.这里发生了什么?我正在使用Windows XP并使用VC++ Express 2010构建.