创建“类";在C中,在堆栈还是在堆上? [英] Creating "classes" in C, on the stack vs the heap?

查看:104
本文介绍了创建“类";在C中,在堆栈还是在堆上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当我看到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 s是它的内存,并返回指向该内存的指针.

每当我不必要地在C ++中看到new时,通常似乎会使C ++程序员发疯,但是这种做法在C中似乎可以接受.这有什么用?为何堆分配的结构类"如此普遍,这背后有什么原因?

解决方案

有多种原因.

  1. 使用不透明"指针
  2. 缺乏破坏者
  3. 嵌入式系统(堆栈溢出问题)
  4. 容器
  5. 惯性
  6. 懒惰"

让我们简短地讨论它们.

对于不透明指针,它使您可以执行以下操作:

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,您不必使用堆-您可以从池,竞技场或诸如此类的东西中进行分配,这可能几乎与堆栈分配一样快,而不会出现下面讨论的潜在堆栈分配问题. /p>

嵌入式系统-您知道堆栈不是无限"资源.当然,对于当今常规"操作系统(POSIX,Windows等)上的大多数应用程序而言,几乎是这样.但是,在嵌入式系统上,堆栈可能只有几个KB.这是极端的,但即使是大型"嵌入式系统也具有以MB为单位的堆栈.因此,如果使用过度,它将耗尽.当这样做时,大多数情况下无法保证会发生什么-AFAIK,在C和C ++中都是未定义的行为". OTOH,CClass_create()在内存不足时可以返回NULL指针,并且可以处理该指针.

容器-C ++用户喜欢堆栈分配,但是,如果在堆栈上创建std::vector,则其内容将被堆分配.您当然可以对其进行调整,但这是默认的行为,它使说容器的所有成员都是堆分配的"变得容易得多,而不是试图弄清楚如果不是它们,该如何处理. /p>

惯性-好的,OO来自SmallTalk.那里的一切都是动态的,因此,对C的自然"翻译就是将所有内容放在堆上"的方式.因此,第一个示例就是这样,并启发了很多人.

"懒惰"-如果您只想要堆栈对象,则需要类似以下内容:

CClass CClass_make();
void CClass_deinit(CClass *me);

但是,如果要同时允许堆栈和堆,则需要添加:

CClass *CClass_create();
void CClass_destroy(CClass *me);

这对于实现者来说是要做的更多工作,但是也会使用户感到困惑.一个人可以创建略有不同的界面,但这并不能改变您需要两组函数的事实.

当然,容器"原因也部分是懒惰"原因.

Whenever I see a C "class" (any struct that is meant to be used by accessing functions that take a pointer to it as the first argument) I see them implemented like this:

typedef struct
{
    int member_a;
    float member_b;
} CClass;

CClass* CClass_create();
void CClass_destroy(CClass *self);
void CClass_someFunction(CClass *self, ...);
...

And in this case CClass_create always mallocs it's memory and returns a pointer to that.

Whenever I see new come up in C++ unnecessarily, it usually seems to drive C++ programmers crazy, but this practice seems acceptable in C. What gives? Is there some reason behind why heap-allocated struct "classes" are so common?

解决方案

There are several reasons for this.

  1. Using "opaque" pointers
  2. Lack of destructors
  3. Embedded systems (stack overflow problem)
  4. Containers
  5. Inertia
  6. "Laziness"

Let's discuss them briefly.

For opaque pointers, it enables you to do something like:

struct CClass_;
typedef struct CClass_ CClass;
// the rest as in your example

So, the user doesn't see the definition of struct CClass_, insulating her from the changes to it and enabling other interesting stuff, like implementing the class differently for different platforms.

Of course, this prohibits using stack variables of CClass. But, OTOH, one can see that this doesn't prohibit allocating CClass objects statically (from some pool) - returned by CClass_create or maybe another function like CClass_create_static.

Lack of destructors - since C compiler will not automatically destruct your CClass stack objects, you need to do it yourself (manually calling the destructor function). So, the only benefit left is the fact that stack allocation is, in general, faster than heap allocation. OTOH, you don't have to use the heap - you can allocate from a pool, or an arena, or some such thing, and that may be almost as fast as stack allocation, without the potential problems of stack allocation discussed below.

Embedded systems - Stack is not an "infinite" resource, you know. Sure, for most applications on today's "Regular" OSes (POSIX, Windows...), it almost is. But, on embedded systems, stack may be as low as a few KBs. That's extreme, but even "big" embedded systems have stack that are in MBs. So, it will run out if over-used. When it does, mostly there is no guarantee what will happen - AFAIK, in both C and C++ that's "Undefined behaviour". OTOH, CClass_create() can return NULL pointer when you're out of memory, and you can handle that.

Containers - C++ users like stack allocation, but, if you create a std::vector on stack, its contents will be heap allocated. You can tweak that, of course, but that is the default behaviour, and it makes ones life much easier to say "all members of a container are heap-allocated" rather than trying to figure out how to handle if they are not.

Inertia - well, the OO came from SmallTalk. Everything is dynamic there, so, the "natural" translation to C is the "put everything on the heap" way. So, the first examples were like that and they inspired others for many years.

"Laziness" - if you know you only want stack objects, you need something like:

CClass CClass_make();
void CClass_deinit(CClass *me);

But, if you want to allow both stack and heap, you need to add:

CClass *CClass_create();
void CClass_destroy(CClass *me);

This is more work to do for the implementer, but is also confusing to the user. One can make slightly different interfaces, but it doesn't change the fact that you need two sets of functions.

Of course, the "containers" reason is also partially a "laziness" reason.

这篇关于创建“类";在C中,在堆栈还是在堆上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆