C++-如何安全地包装malloc或使用新运算符进行模拟
C++ - How to safely wrap malloc or emulate with the new operator
在C++中的函数中封装malloc
有一种公认的安全方法吗?我试图做的是分配任意大小的内存块,用于保存将二进制值写入固定大小缓冲区(即:机器指令)的函数的输出。我目前使用的方法是这样的:
class voidp {
void *p;
public:
voidp (void *pp) : p (pp) {}
template<class T> operator T *() { return (T *) p; }
};
When converting C to C++, you can define malloc like this:
inline voidp
mymalloc (size_t size)
{
return malloc (size);
}
#define malloc mymalloc
在许多情况下,似乎不允许显式转换为(void*)
(至少在我的编译器中是这样),我必须使用上面基于模板的方法。
这种方法安全吗?对于"三条规则"(即:是否有任何构造函数需要禁用或明确定义/重载),是否有任何含义?
谢谢。
参考
- 我对C++运算符new的看法-在类型系统中打一个整体,访问2014-09-05,
<http://www.scs.stanford.edu/~dm/home/papers/c++-new.html>
- 解决C面试中困扰我的记忆对齐问题,访问2014-09-05,
<https://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me>
编辑
我这样做的原因是,我试图使用比通过void* buffer = new char[100]
分配一个字符块更好的东西。为了进一步详细说明,我正在编写一些较低级别的代码,根据我的建议,必须用C++而不是纯C编写。我还需要有动态内存分配方法,在堆上创建16字节、32字节和64字节对齐的内存块,就像下面的16字节对齐示例一样。
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
我的应用程序在块中创建了一组低级别的机器指令,这些指令必须是16/32/64字节对齐的(否则会触发CPU级别的错误),然后将它们传递给板载DSP芯片,后者将它们作为实际指令运行,而不仅仅是输入数据。
主题1:旧的好void*
C++的类型比C强。这不是问题,而是解决方案编译器以这种方式截获了许多令人讨厌的问题,这些问题在调试时需要数小时才能发现。
简单的例子。下面的代码在C中编译。当我执行时,我得到一个核心转储。
FILE *fp; // file
void *pbuff; // pointer to buffeer
pbuff = malloc(BUFSIZ);
fp=fopen("test.txt", "rw");
fread(fp, BUFSIZ, 1, pbuff); // ooops !! Did you notice ?
为什么?我把fp和pbuff倒过来。两者都是void*
至anything*
和anything*
至void*
构建体的受害者。当然,经过实验的程序员不会在标准库上犯这样的错误!但剩下的怎么办?
使用C++时,这个错误代码不会编译,因为将void*
pbuff转换为FILE*
被认为是一个问题。
主题2:模板作废是一种安全的做法吗
C++有类和继承性。还有一大堆类型转换来突出显示你可以(现在仍然允许)做的奇怪的指针操作。C++再次简化了错误查找。例如,reinterpret_cast<>
比控制更严格的static_cast<>
更值得注意
要做安全的事情,需要理解C++对象模型并使用适当的工具。您的voidp()
非常聪明,但它不是OOP的合适工具。
简单的例子,首先是原生方式:
struct A { std::string name; A(std::string s) : name(s) {} };
struct B : A { int x, y; B(std::string s, int a, int b) : A{ s }, x(a), y(b) {} };
...
A a{ "Chris" };
B b{ "Tophe", 30, 40 };
A *gpa = &a;
B *gpb = &b;
gpa = gpb; // yes, works, because a B is an A and compiler knows it.
//gpb = gpa; // No ! you can't without a recast !
现在使用voidp
方法:
voidp pa(&a);
voidp pb(&b);
pb = pa; // no problem.
gpb = pa; // no problem ! Not even a cast to draw attention on potential issue !
所以你看,这相当不安全它真的隐藏了令人讨厌的错误!
主题3:malloc()
比new
好吗
老实说,使用malloc()
,您可以轻松创建任何内容。而且很容易制造出错误尺寸的东西。。。。使用new
,你也可以做一些奇怪的事情,但更难做基本的错误。
new
可以调用对象构造函数。使用malloc()
,您需要额外的步骤才能做到这一点。
然后,当你有malloc()
时,你就有了free()
。malloc()
/free()
非常适合C中的被动数据结构。但在C++中,当你想正确地去除一个对象时,你必须去存储它。所以delete
确实更合适。
结论
我饶有兴趣地阅读了你的推荐信。的确,new
有局限性。
但我不同意这篇文章的结论。与其避免new
并切换回malloc()
(同样:它非常适合C代码,但根本不适合C++),它将是研究标准库的更好方法,例如使用shared_ptr<>
和其他智能指针。这些对于避免内存泄漏来说比重写自己版本的malloc要好得多。。。
编辑
你所做的具体用途可以在C++和中完成
char buffer[1024 + 15]; // allocation on stack, or char *mem = new char[1024+15]
char *ptr = reinterpret_cast<char*>(((uintptr_t)&buffer + 15) & ~(uintptr_t)0x0F); // Yes
std::fill_n(ptr, 0, 1024); // is memset_16aligned() really portable ?
// nothing if on stack // or delete[] mem if new was used
还有一个函数std::align()
,用于计算ptr。还有一个新的位置可以在固定地址上分配C++对象,例如:
char* p = new(ptr)char[1024]; // of course you shouldn't delete this one
如果您正在编写C++代码。您应该使用new和delete。用C++编写C风格通常是不好的做法,因为C++编译器不会生成最佳的二进制文件。如果你发现自己用void做了很多类型的铸造,那么你可能做错了。
相反,使用C++11智能指针!
new
和delete
用于低级别库开发。有STL容器可用于缓冲区分配和取消分配。
在您的情况下,您可以尝试
std::vector<unsigned char> myBuffer(MAX_SIZE);
- 如何使用Google Mock来模拟gettimeofday()
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 增量运算符与后缀混淆
- 一个关于在C++中重载布尔运算符的问题
- 运算符C++ "delete []"仅删除 2 个前值
- G锁定铸造到基础上会释放模拟行为
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 模拟 C++ 中 lambda 的复制赋值运算符
- 是否可以完全模拟虚拟运算符++(int)?
- 重载 + 运算符以模拟移动到下一个节点
- 使用 google mock 模拟转换运算符 int()
- 正确模拟 [] 运算符
- 使用GMOCK模拟c++中的新运算符
- C++-如何安全地包装malloc或使用新运算符进行模拟
- 是否重载索引运算符以模拟POD多维数组
- 如何使用运算符[]创建模拟类