The*_*ang 47 c heap stack struct
每当我看到一个C"类"(通过访问将指针作为第一个参数的函数来使用的任何结构)时,我看到它们实现如下:
typedef struct
{
int member_a;
float member_b;
} CClass;
CClass* CClass_create();
void CClass_destroy(CClass *self);
void CClass_someFunction(CClass *self, ...);
...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,它CClass_create
始终malloc
是内存并返回指向它的指针.
每当我看到new
C++不必要地出现时,它似乎通常会让C++程序员疯狂,但这种做法在C中似乎是可以接受的.为什么堆分配的结构"类"如此常见?
srd*_*vic 50
有几个原因.
我们来简单讨论一下.
对于不透明指针,它使您可以执行以下操作:
struct CClass_;
typedef struct CClass_ CClass;
// the rest as in your example
Run Code Online (Sandbox Code Playgroud)
因此,用户没有看到它的定义struct CClass_
,使她免受对它的更改并启用其他有趣的东西,比如为不同的平台实现不同的类.
当然,这禁止使用堆栈变量CClass
.但是,OTOH,可以看出这并不禁止CClass
静态地(从某个池中)分配对象 - 由CClass_create
或者可能是另一个函数返回CClass_create_static
.
缺少析构函数 - 由于C编译器不会自动破坏您的CClass
堆栈对象,您需要自己完成(手动调用析构函数).因此,剩下的唯一好处是堆栈分配通常比堆分配更快.OTOH,您不必使用堆 - 您可以从池,竞技场或某些此类事物进行分配,这可能几乎与堆栈分配一样快,而不会出现下面讨论的堆栈分配的潜在问题.
嵌入式系统 - 堆栈不是"无限"资源,你知道.当然,对于今天的"常规"操作系统(POSIX,Windows ......)上的大多数应用程序,它几乎都是.但是,在嵌入式系统上,堆栈可能低至几KB.这是极端的,但即使是"大"的嵌入式系统也有以MB为单位的堆栈.因此,如果过度使用它将会耗尽.当它发生时,大多数情况下无法保证会发生什么--AFAIK,在C和C++中都是"未定义的行为".OTOH,CClass_create()
当你内存不足时可以返回NULL指针,你可以处理它.
容器 - C++用户喜欢堆栈分配,但是,如果你创建一个std::vector
堆栈,它的内容将被堆分配.当然,您可以调整一下,但这是默认行为,它使人们更容易说"容器的所有成员都是堆分配的",而不是试图弄清楚如何处理它们.
惯性 - 好吧,OO来自SmallTalk.那里的一切都是动态的,因此,对C的"自然"翻译是"把所有东西放在堆里"的方式.所以,第一个例子就是这样,他们多年来一直鼓舞他人.
" 懒惰 " - 如果你知道你只想要堆栈对象,你需要这样的东西:
CClass CClass_make();
void CClass_deinit(CClass *me);
Run Code Online (Sandbox Code Playgroud)
但是,如果要同时允许堆栈和堆,则需要添加:
CClass *CClass_create();
void CClass_destroy(CClass *me);
Run Code Online (Sandbox Code Playgroud)
这对于实现者来说是更多的工作,但也使用户感到困惑.可以使接口略有不同,但它不会改变您需要两组功能的事实.
当然,"容器"的原因也部分是"懒惰"的原因.
Jab*_*cky 14
假设,如你的问题CClass_create
和CClass_destroy
使用malloc/free
,那么对我来说,做以下是不好的做法:
void Myfunc()
{
CClass* myinstance = CClass_create();
...
CClass_destroy(myinstance);
}
Run Code Online (Sandbox Code Playgroud)
因为我们可以轻松地避免使用malloc和free:
void Myfunc()
{
CClass myinstance; // no malloc needed here, myinstance is on the stack
CClass_Initialize(&myinstance);
...
CClass_Uninitialize(&myinstance);
// no free needed here because myinstance is on the stack
}
Run Code Online (Sandbox Code Playgroud)
同
CClass* CClass_create()
{
CClass *self= malloc(sizeof(CClass));
CClass_Initialize(self);
return self;
}
void CClass_destroy(CClass *self);
{
CClass_Uninitialize(self);
free(self);
}
void CClass_Initialize(CClass *self)
{
// initialize stuff
...
}
void CClass_Uninitialize(CClass *self);
{
// uninitialize stuff
...
}
Run Code Online (Sandbox Code Playgroud)
在C++中我们也宁愿这样做:
void Myfunc()
{
CClass myinstance;
...
}
Run Code Online (Sandbox Code Playgroud)
比这个:
void Myfunc()
{
CClass* myinstance = new CCLass;
...
delete myinstance;
}
Run Code Online (Sandbox Code Playgroud)
为了避免不必要的new
/ delete
.
小智 9
在C中,当某个组件提供"创建"功能时,组件实现者也可以控制组件的初始化方式.所以它不仅模仿 C++' operator new
而且模仿类构造函数.
放弃对初始化的这种控制意味着对输入进行更多错误检查,因此保持控制可以更容易地提供一致且可预测的行为.
我也malloc
常常被用来分配内存.通常情况可能如此,但并非总是如此.例如,在某些嵌入式系统中,您会发现malloc
/ free
根本不使用/ .这些X_create
函数可以以其他方式分配,例如,从编译时大小固定的数组中分配.
小智 8
这产生了很多答案,因为它有点基于意见.我仍然想解释为什么我个人更喜欢在堆上分配我的"C对象".原因是我的字段全部隐藏(说话:私有)消费代码.这称为不透明指针.实际上,这意味着您的头文件没有定义struct
正在使用中,它只声明它.直接后果是,消耗代码无法知道其大小,struct
因此堆栈分配变得不可能.
好处是:消耗代码永远不会依赖于定义struct
,这意味着你不可能以某种方式struct
从外部呈现不一致的内容,并且避免在struct
更改时不必要地重新编译消耗代码.
第一个问题在c ++中通过声明字段来解决private
.但是class
仍然会在使用它的所有编译单元中导入您的定义,这使得有必要重新编译它们,即使只有您的private
成员发生更改.在c ++中经常使用的解决方案是pimpl
模式:将所有私有成员放在第二个struct
(或class
:)中,仅在实现文件中定义.当然,这需要pimpl
在堆上分配.
除此之外:现代 OOP语言(例如java或c#)具有分配对象(通常在内部决定它是堆栈还是堆)的方法,而不需要调用代码知道它们的定义.
归档时间: |
|
查看次数: |
5102 次 |
最近记录: |