首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在C中创建“类”,在堆栈上还是在堆上?

在C中创建“类”,在堆栈上还是在堆上?
EN

Stack Overflow用户
提问于 2015-07-28 10:00:58
回答 12查看 6.2K关注 0票数 48

每当我看到一个C“类”(任何用于访问以指向它的指针作为第一个参数的函数的结构)时,我都会看到它们的实现方式如下:

代码语言:javascript
运行
复制
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中似乎是可以接受的。为什么堆分配的结构“类”如此常见有什么原因吗?

EN

回答 12

Stack Overflow用户

回答已采纳

发布于 2015-07-28 10:51:02

这有几个原因。

  1. 使用“不透明”指针
  2. 缺乏析构函数
  3. 嵌入式系统(堆栈溢出问题)
  4. 容器
  5. 惯性
  6. “懒惰”

让我们简短地讨论一下。

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

代码语言:javascript
运行
复制
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的“自然”翻译是“将所有东西放在堆上”的方式。所以,最初的例子是这样的,他们激励了很多年的其他人。

“惰性”--如果您知道只需要堆栈对象,则需要以下内容:

代码语言:javascript
运行
复制
CClass CClass_make();
void CClass_deinit(CClass *me);

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

代码语言:javascript
运行
复制
CClass *CClass_create();
void CClass_destroy(CClass *me);

对于实现者来说,这是更多的工作,但也让用户感到困惑。一个用户可以创建稍微不同的接口,但这并不会改变您需要两组函数的事实。

当然,“容器”的原因也部分是“懒惰”的原因。

票数 51
EN

Stack Overflow用户

发布于 2015-07-28 10:13:44

假设,就像在您的问题中一样,CClass_createCClass_destroy使用malloc/free,那么对于我来说,以下操作是错误的做法:

代码语言:javascript
运行
复制
void Myfunc()
{
  CClass* myinstance = CClass_create();
  ...

  CClass_destroy(myinstance);
}

因为我们可以很容易地避免一个malloc和一个自由:

代码语言:javascript
运行
复制
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
}

使用

代码语言:javascript
运行
复制
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++中,我们也宁愿这样做:

代码语言:javascript
运行
复制
void Myfunc()
{
  CClass myinstance;
  ...

}

比这更重要:

代码语言:javascript
运行
复制
void Myfunc()
{
  CClass* myinstance = new CCLass;
  ...

  delete myinstance;
}

以避免不必要的new/delete

票数 14
EN

Stack Overflow用户

发布于 2015-07-28 10:17:30

在C中,当某些组件提供"create“函数时,组件实现者也控制着组件的初始化方式。因此,它不仅模拟了C++的operator new,而且还模拟了类构造函数。

放弃对初始化的控制意味着对输入进行更多的错误检查,因此保持控制更容易提供一致和可预测的行为。

我也不同意malloc 总是使用来分配内存。这种情况可能经常发生,但并非总是如此。例如,在某些嵌入式系统中,您会发现根本不使用malloc/freeX_create函数可以以其他方式分配,例如在编译时大小固定的数组。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31673065

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档