生成一个C++对象的成本

最近两年C用得多了,C++有些生疏,又常常用PYTHON,或者阅读些JAVA的代码,感觉C的开发者们由于C语言在软件工程上的先天缺陷,导致开发效率不高,所以决定拿出C++来看看用用,准备把libevent封装出一个类ACE的C++实现,首先来复读下C++对象模型吧。要了解new一个object的成本,最主要的就是知道,编译器会给对象分配多少内存,知道C++的对象模型无疑就了解这一点了。


如果要研究C++的对象模型,大家潜意识都想知道的是,C++比C好在哪里?又比C差在哪里?

我们主要就是想从C++的对象模型里找到后一个答案。前一个答案在软件工程中是毫无疑义的,面向对象的优越性要比C语言里一堆数据结构+和一堆可能与它们相关的函数,可读性、可用性好很好,对开发大型软件工程,需要几百人开发一个项目来说,C++好太多了。看看JAVA或者python程序员们,他们为什么可以一直站在巨人的肩膀上,想完成任何一个功能都超级方便的调用大师们以前写好的package/API,借用各种设计模式,应用级别程序员们可以非常EASY的使用复杂的设计,一些只有高级C程序员才能掌握的东东。当然,JAVA的很多特性也导致不适应核心服务器的开发,比如它的垃圾回收机制。

 

OK,闲话少叙,在看对象模型前,先看几个C++与C语言的典型不同之处。

1、自然是类的定义了,最大的改变就是类把数据结构与方法捆到一起了,可读性上提升巨大。对成员变量和成员方法,有5种类型:static member, nonstatic member, static function, nonstatic function, virtual function.

2、继承,这里很有许多细节了,核心解决问题就是动态绑定,也就是virtual关键字。virtual出现的唯一原因就是为了解决继承机制,否则struct里引入方法就足够了,class出现就是为了这。virtual关键字解决了子类实例和父类实例的一些特殊关系,考虑以下场景:软件工程中,很喜欢每个模块专注于自己的事,尽量忽略与自己无关的实现,这样,很可能会用一个父类指针,该指针太可能指向多种不同的子类了,但是现在,使用这个抽象父类指针的模块不想关注细节,当它调用对象的某个方法时,到底是调用父类的方法还是子类的方法呢?动态绑定这个特性就是,开发者可以决定这一点,当你用virtual关键字申明父类方法时,如果子类重定义了该方法,如果这个指针实际指向的是某子类对象,那么调用的方法一定是该子类方法的实现。

 

举个例子吧,就像什么析构函数总喜欢写成virtual?这个例子应该容易说明virtual的玩法。一段简单的代码:

 

#include <iostream> using namespace std; class Father { public: int m_fMember; Father(){m_fMember=1;} ~Father(){cout<<m_fMember<<endl;} }; class Child : public Father{ public: int m_cMember; Child(){m_cMember=2;} ~Child(){cout<<m_cMember<<endl;} }; int main(int argc, char** argv) { Father* pObj1 = new Child(); delete pObj1; Child* pObj2 = new Child(); delete pObj2; return 0; }
这段代码的结果是1 2 1,啥意思呢?就是说,如果不用virtual函数,是没有执行期绑定一说的,比如pObj1这个指针,其实它是Child对象,但是在释放时,~Child()方法并没有被调用,仅调用了~Father方法。为什么呢?因为没有用virtual,就是编译期绑定,当你在编译时gcc/g++只知道pObj1是个Father对象,所以在delete时就去调用Father的析构了。而如果定义成virtual ~Father时,结果就是一定会析构Child,这就是为什么析构函数都要用virtual,因为没人知道会不会有子类继承,否则一旦继承,发生这样的事,析构函数里万一释放了些资源,比如SOCKET,比如memory,那就是资源泄露了。

 

 

那么以上,C++对象模型是怎么做到的呢?画张象征性的图吧。先定义一个类,再看看它的内存布局:

 

class Father { public: int m_fMember; static int m_sMember; static void testSFunc(){} void testFunc(){} virtual void testVFunc(){} Father(){m_fMember=1;} virtual ~Father(){cout<<m_fMember<<endl;} };

我们生成一个Father对象,看看它的的内存布局是啥样的(同志们,这只是近似存储布局图,没有把编译和运行的差别放上去,下篇再讲这个):

这里大家明白了吧?即使一个Child对象在编译时被赋为Father类型,但是实际调用时,virtual方法会被单独的拎出来,在vtbl中指向实际的实现,所以,该对象在delete时会调用Child的析构函数,而如果你像上面例子那样,析构方法不使用virtual,将会用到上图中的最后一个指针,指向类成员函数里,这样就不是执行期绑定了。

 

剩下的static成员(还有所有的正常成员函数),都是与对象实例无关的内存布局。这样,其实如果不使用virtual,C++比之C并没有增加成本,尽可放心使用。

 

 

<think>首先,用户的问题是关于C++和C在面向对象方面的差异。系统指令要求我使用中文回答,并遵循特定的格式:行内数学表达式用$...$,独立公式用$$...$$单独成段。同时,我需要生成相关问题。 回顾引用: - 引用[1]:讨论了C++面向对象与面向过程的区别与联系。提到面向过程的优点是性能高,因为类调用需要实例化,开销大;适用于单片机、嵌入式开发等。缺点是维护性差。 - 引用[2]:解释了面向过程和面向对象的设计例子,如外卖系统。关注点不同:面向过程关注函数实现,面向对象关注对象的行为。 用户的问题明确是“C++和C在面向对象方面的区别”。C是面向过程的语言,而C++是面向对象的语言,支持类和对象。 关键点: - C是过程式编程语言,专注于函数和模块。 - C++支持面向对象编程(OOP),包括类、对象、继承、多态、封装等特性。 我需要构建一个清晰的回答结构: 1. **介绍差异**:解释C和C++在OOP方面的核心区别。 2. **详细对比**: - C没有内置的OOP特性;使用结构体和函数模拟。 - C++有类(class)、继承(inheritance)、多态(polymorphism)、封装(encapsulation)。 3. **举例说明**:使用简单例子比较两者。 4. **优缺点**:基于引用提到性能和维护性。 5. **应用场景**:C用于性能关键系统,C++用于需要抽象和复用的系统。 6. **生成相关问题**:在末尾添加相关问题。 遵循指令: - 行内数学表达式:如果有数学内容,使用$...$。但这里可能不需要数学表达式,除非谈论时间复杂度等。 - 独立公式:如果需要,单独成段。 - LaTeX语法正确。 - 中文回答。 - 生成相关问题:添加到末尾。 - 引用标识:如果在回答中提到引用内容,末尾添加如[^1]。 在回答中,我需要引用用户提供的引用内容。引用[1]和[^2]是关于区别的。 结构回答: - 开头:简述差异。 - 主体: - **核心特性**:C++支持类等OOP特性,C不支持。 - **模拟OOP在C中**:C可以用结构体和函数指针模拟,但不原生支持。 - **具体差异**: - 封装:C++使用访问修饰符(private, public, protected),C没有。 - 继承:C++支持单继承和多继承,C不支持。 - 多态:C++支持虚函数实现多态,C需要手动模拟。 - **性能对比**:引用[1]提到C++性能开销,但优化后可能接近C。 - **例子**:引用[2]的例子,扩展说明。 - 结尾:总结并生成相关问题。 相关问题: 1. 如何用C模拟面向对象特性? 2. C++的继承机制有哪些类型? 3. 在性能关键应用中,如何权衡使用C或C++? 确保回答真实可靠:基于标准知识。 最终输出格式: - 回答问题清晰。 - §§相关问题§§部分添加。 </think>### C++与C在面向对象方面的差异 C语言是一种面向过程的编程语言,而C++则扩展了C的功能,引入了面向对象编程(OOP)的特性。它们在面向对象方面的主要区别体现在核心概念、语法支持和适用场景上。以下我将逐步解释这些差异,基于您的查询和参考引用内容。 #### 1. **核心概念差异** - **C语言(面向过程)**: 专注于函数和模块的实现。例如,解决问题时会将逻辑分解为独立的函数序列(如点餐、接单、送餐的函数)。C中没有类的概念,数据和函数是分离的。尽管可以用结构体(`struct`)和函数指针模拟简单的对象行为,但这需要手动管理,缺乏原生支持。引用[2]中提到,面向过程设计关注函数实现,这在C中很常见,优点是性能高(因为避免了实例化开销),但维护和扩展性较差。 - **C++(面向对象)**: 支持OOP的四大支柱:封装、继承、多态和抽象。C++提供了类(`class`)作为核心特性,允许将数据和操作数据的函数绑定在一起。例如,在引用[2]的外卖系统例子中,C++可以定义`User`类(封装点餐行为)、`Vendor`类(封装接单行为)等对象模型,自然地处理过程。这提高了代码的可维护性、可复用性和可扩展性。但引用[1]指出,类调用需要实例化,可能带来性能开销(如资源消耗)。 #### 2. **语法特性对比** 以下是关键语法差异的总结: - **封装**: - C:只能通过结构体(`struct`)定义数据,函数需单独声明和实现。访问控制无限制(所有成员公开),可能导致数据不安全。 - C++:使用`class`关键字,支持访问修饰符(如`private`、`public`),实现数据隐藏和保护。例如: ```cpp // C++示例:封装 class User { private: string name; // 私有数据 public: void orderFood() { /* 点餐行为 */ } // 公有方法 }; ``` 在C中,类似功能需用结构体和全局函数模拟,但无法强制封装。 - **继承**: - C:无原生继承支持。只能通过组合结构体或函数指针链来模拟,代码冗余且易出错。 - C++:支持单继承和多继承,派生类可重用基类成员。例如: ```cpp // C++示例:继承 class Vendor : public User { // Vendor继承自User public: void acceptOrder() { /* 接单行为 */ } }; ``` 这体现了面向对象“关注对象行为”的优势(引用[2]),使得代码更易扩展。 - **多态**: - C:需手动使用函数指针实现动态绑定(如定义函数指针表),但类型安全差,维护困难。 - C++:通过虚函数(`virtual`)和运行时类型识别(RTTI)实现多态。例如: ```cpp // C++示例:多态 class Rider { public: virtual void deliver() { /* 默认送餐行为 */ } }; class FastRider : public Rider { public: void deliver() override { /* 定制送餐行为 */ } }; ``` 这允许“骑手对象”根据不同子类执行不同行为,而在C中类似功能需复杂手写逻辑。 - **其他特性**: - C++还支持运算符重载、模板等OOP扩展,而C仅限于基本的函数式编程。 - 性能方面:引用[1]提到,C++的OOP特性可能导致开销(如虚函数表),但编译器优化(如内联)可减少差距;C直接函数调用更高效,适合嵌入式系统。 #### 3. **优缺点和适用场景** - **性能**:C语言在性能关键领域(如单片机、嵌入式开发)占优,因为它避免了实例化开销(引用[1])。C++的OOP增加了灵活性,但可能消耗更多资源。 - **维护性**:C++的OOP增强了代码组织和复用,降低维护成本;C的面向过程代码在大型项目中易混乱。 - **应用场景**: - C:适合操作系统内核、实时系统(如Linux/Unix开发),其中性能优先。 - C++:适合GUI应用、游戏开发或复杂系统(如外卖平台),需求高扩展性和抽象。 总之,C++通过引入OOP特性显著提升了代码的抽象能力,而C更注重高效的过程控制。两者并非互斥——C++兼容C语法,但OOP使C++更适合现代软件工程[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值