每当我看到一个C“类”(任何用于访问以指向它的指针作为第一个参数的函数的结构)时,我都会看到它们的实现方式如下:
typedef struct
{
int member_a;
float member_b;
} CClass;
CClass* CClass_create();
void CClass_destroy(CClass *self);
void CClass_someFunction(CClass *self, ...);
...在本例中,CClass_create总是malloc的内存,并返回指向该内存的指针。
每当我看到new不必要地出现在C++上时,它通常看起来会让C++程序员发疯,但这种做法在C中似乎是可以接受的。为什么堆分配的结构“类”如此常见有什么原因吗?
发布于 2015-07-28 10:51:02
这有几个原因。
让我们简短地讨论一下。
对于不透明指针,它允许您执行以下操作:
struct CClass_;
typedef struct CClass_ CClass;
// the rest as in your example因此,用户没有看到struct CClass_的定义,使她不受对它的更改的影响,并启用其他有趣的东西,比如为不同的平台不同地实现类。
当然,这禁止使用CClass的堆栈变量。但是,OTOH可以看出,这并不禁止静态地(从某个池中)分配CClass对象--由CClass_create或其他类似CClass_create_static的函数返回。
缺乏析构函数(析构函数)--因为C编译器不会自动解构您的CClass堆栈对象,所以您需要自己去做(手动调用析构函数)。因此,剩下的唯一好处是堆栈分配通常比堆分配更快。OTOH,您不必使用堆--您可以从一个池、一个竞技场或其他类似的地方分配,这可能与堆栈分配一样快,而不会出现下面讨论的堆栈分配的潜在问题。
嵌入式系统- Stack不是一个“无限”的资源,你知道。当然,对于今天的“常规”OSes (POSIX,Windows.)上的大多数应用程序来说,它几乎都是。但是,在嵌入式系统上,堆栈可能低到几个KBs。这是极端的,但是即使是“大型”嵌入式系统也有MBs的堆栈。所以,如果过度使用它就会用完。如果是这样的话,很大程度上无法保证会发生什么- AFAIK,在C和C++中都是“未定义的行为”。OTOH,CClass_create()可以在内存不足时返回空指针,您可以处理这个问题。
Containers - C++用户喜欢堆栈分配,但是,如果在堆栈上创建std::vector,则其内容将被堆分配。当然,你可以调整它,但这是默认的行为,它使你的生活更容易说“一个容器的所有成员都是堆分配的”,而不是试图找出如何处理如果他们没有。
惯性-哦,OO来自SmallTalk。那里的一切都是动态的,所以,C的“自然”翻译是“将所有东西放在堆上”的方式。所以,最初的例子是这样的,他们激励了很多年的其他人。
“惰性”--如果您知道只需要堆栈对象,则需要以下内容:
CClass CClass_make();
void CClass_deinit(CClass *me);但是,如果希望同时允许堆栈和堆,则需要添加:
CClass *CClass_create();
void CClass_destroy(CClass *me);对于实现者来说,这是更多的工作,但也让用户感到困惑。一个用户可以创建稍微不同的接口,但这并不会改变您需要两组函数的事实。
当然,“容器”的原因也部分是“懒惰”的原因。
发布于 2015-07-28 10:13:44
假设,就像在您的问题中一样,CClass_create和CClass_destroy使用malloc/free,那么对于我来说,以下操作是错误的做法:
void Myfunc()
{
CClass* myinstance = CClass_create();
...
CClass_destroy(myinstance);
}因为我们可以很容易地避免一个malloc和一个自由:
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
}使用
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
...
}在C++中,我们也宁愿这样做:
void Myfunc()
{
CClass myinstance;
...
}比这更重要:
void Myfunc()
{
CClass* myinstance = new CCLass;
...
delete myinstance;
}以避免不必要的new/delete。
发布于 2015-07-28 10:17:30
在C中,当某些组件提供"create“函数时,组件实现者也控制着组件的初始化方式。因此,它不仅模拟了C++的operator new,而且还模拟了类构造函数。
放弃对初始化的控制意味着对输入进行更多的错误检查,因此保持控制更容易提供一致和可预测的行为。
我也不同意malloc 总是使用来分配内存。这种情况可能经常发生,但并非总是如此。例如,在某些嵌入式系统中,您会发现根本不使用malloc/free。X_create函数可以以其他方式分配,例如在编译时大小固定的数组。
https://stackoverflow.com/questions/31673065
复制相似问题