被误解的C++——类型

longshanks 2007-06-26 05:01:27
类型
任何一种语言都有类型。对类型的不同的态度,造就了语言的个性。我们通常会将语言分为“强类型”和“弱类型”。
通常认为C++是强类型的。但也有反对意见。反对者认为,既然C++拥有隐式类型转换,那么就不该作为强类型语言。我这里不打算趟这潭混水,强类型还是弱类型,没有什么实际意义。
这里,我打算认真地考察一下C++独特的类型系统,来探寻C++在语言中特立独行的根源。我会尽可能不涉及语言的比较,至少不涉及他们的好坏,以免引发新一轮的口水仗。
强类型提供了很好的类型安全,但缺少灵活性。弱类型化后,灵活性提高了,但类型安全无法保障。C++所作的探索,就是寻找一种方式,在强类型的情况下,允许提供灵活,但又安全的类型系统。
让我们先从C++的内置类型说起。
C++的类型分为内置类型和用户定义类型。内置类型主要包括整数类型、浮点类型、引用类型(指针和引用)等等。我们先来分析一下内置类型,以整数类型为例。
我们知道,一个整数类型可以进行初始化、赋值、算术运算、比较、位操作,以及参与逻辑运算:
int a=10; //初始化
int b;
b=a; //赋值
int c=(a+b)*(a-b); //算术运算
if(c==b) … //比较
a=c&b; //位操作
if(c==b || !a) … //逻辑运算
当然,其他的还包括取地址、取引用等类型的基本操作。
这些操作都是语言赋予整数类型的基本操作,我们无需对其进行而外的转换或者处理。但是,当我们把目光转向用户定义类型后,问题就复杂化了。由于C++被定位于系统级开发语言(实际上C++什么开发领域都可以胜任,但最初发明它时是打算用于开发系统软件的),所以时常会需要一些古怪的操作,比如把一个用户定义类型赋值给int类型,这种操作在强类型语言中是不合规矩的。
如果我们不管三七二十一,把用户定义类型按位拷贝给int类型(这是int类型之间赋值操作的语义),那么准保会惹上大麻烦的。但如果在特定情况下,这种操作是需要的(当然不一定是必需的)。那么,我们就应当提供一种方法,允许这种赋值操作在受控的情况下进行。
为此,C++引入了操作符重载(学自Ada),以及一些相关的机制。通过这些机制,使我们(几乎)可以按照内置类型(如整数)的行为设计用户定义的类型。下面我通过一个案例慢慢讲述如何把一个用户类型变成内置类型的模仿者。这个案例来源于前些日子论坛上的口水仗,就是开发variant类型。为了简化问题,我选取了三种具有代表性的类型int,double,char*作为variant包容的目标,并且不考虑性能问题。
首先,我定义了一个枚举,为了使代码能够更加清晰:
enum
{
vt_empty=-1, //空variant
vt_double=0, //double类型
vt_int=1, //int类型
vt_string=2 //字符串类型
};
然后,定义variant的基本结构。我使用了最传统的手法,union。
class variant
{
private:
int var_type; //variant包含的类型标记
union
{
double dbval;
int ival;
char* csval; //由于union不能存放拥有non-trivial构造函数等成员,
// 所以只能用char*,提取数据时另行处理
};
};
现在,我们一步步使variant越来越像一个内置类型。看一下int类型的初始化方式:
int a(0), b=0;
int(0); //创建并初始化一个int临时对象
我们先来考虑用一个variant对象初始化另一个variant对象。实现这个功能,需要通过重载构造函数:
class variant
{
public:
variant(const variant& v) {…}

};
这是一个拷贝构造函数,使得我们可以用一个variant对象初始化另一个variant对象:
variant x(a), y=a; variant(a); //假设a是一个拥有有效值的variant对象
如果我们没有定义任何构造函数,那么编译器会为我们生成一个复制构造函数。但这不是我们要的,因为编译器生成的复制构造函数执行浅拷贝,它只会将一个对象按位赋值给另一个。由于variant需要管理资源引用,必须执行深拷贝,所以必须另行定义一个赋值构造函数。
按C++标准,一旦定义了一个构造函数,那么编译器将不会再生成默认构造函数。所以为了能够如下声明对象:
variant x;
我们必须定义一个默认构造函数:
class variant
{
public:
variant(): var_type(vt_empty) {…}

};
下一步,实现variant对象间的赋值。C++中内置类型的对象间赋值使用=操作符:
int a=100, b;
b=a;
用户定义的类型间的赋值也使用=操作符。所以,只需重载operator=便可实现对象间的赋值:
class variant
{
public:
variant& operator=(const variant& v) {…}

};
variant x, y;
x=y;
int是一种可以计算的数值类型。所以,我们可以对int类型的变量执行算术运算、比较、逻辑运算、位运算等:
int a、b、c、d、e、f、g;
a=b+c;
d=a-b;
e/=c;
c==d;
if(!c) …
f=f<<3;

同样,variant涵盖了几种数值类型,那么要求其能够进行这些运算,也是理所当然的:
variant a、b、c、d、e、f、g;
a=b+c;
d=a-b;
e/=c;
c==d;
if(!c) …
f=f<<3;

为实现这一点,C++提供了大量的操作符重载。在C++中,除了“.” 、“.*”、“? :”、“#”、“##”五个操作符,RTTI操作符,以及xxx_cast外,其余都能重载。操作符可以作为类的成员,也可以作为全局函数。(类型转换操作符和“=”只能作为类的成员)。通常,将操作符重载作为全局函数更灵活,同时也能避免一些问题。
我们先重载操作数都是variant的操作符:
bool operator==(const variant& v1, const variant& v2) {…}
bool operator!=( const variant& v1, const variant& v2) {…}
variant& operator+=( const variant& v1, const variant& v2) {…}
variant operator+( const variant& v1, const variant& v2) {…}

需要注意的是,对与variant而言,他可能代表了多种不同的类型。这些类型间不一定都能进行运算。所以,variant应当在运算前进行类型检查。不匹配时,应抛出运行时错误。
C++允许内置类型按一定的规则相互转换。比如:
int a=100;
double b=a;
a=b; //可以转换,但有warning
为了使variant融入C++的类型体系,我们应当允许variant同所包容的类型间相互转换。C++为我们提供了这类机制。下面我们逐步深入。
我们先处理初始化。非variant类型初始化也是通过重载构造函数:
class variant
{
public:
variant(double val) {…}
variant(int val) {…}
variant(const string& val) {…}

}
这些是所谓的“类型转换构造函数”。它们接受一个其它类型的对象作为参数,在函数体中执行特定的初始化操作。最终达到如下效果:
int a=10;
double b=23;
string c(“abc”);
variant x(a), y=b; variant(c);
接下来,处理不同类型和variant对象赋值的问题。先看向variant对象赋值。同样通过=操作符:
class variant
{
public:
variant& operator=(double v) {…}
variant& operator=(int v) {…}
variant& operator=(const string& v) {…}
variant& operator=(const char*) {…}//该重载为了处理字符串常量

};
这样,便可以如下操作:
int a=10;
double b=23;
string c(“abc”);
variant x,y,z;
x=a;
y=b;
z=c;
然后再看由variant对象向其它类型赋值。实现这种操作需要利用类型转换操作符:
class variant
{
public:
operator double() {…}
operator int() {…}
operator string() {…}

};
使用起来和内置类型赋值或初始化一样:
variant x(10), y(2.5), z(“abc”);
int a=x;
double b=y;
string c;
c=z;
现在,variant已经非常“象”内置类型了。最后只需要让variant同其它类型一起参与运算便大功告成了。我们依然需要依靠操作符重载,不过此处使用全局函数方式的操作符重载:
bool operator==(const variant& v1, int v2){…}
bool operator==(int v1, const variant& v2){…}
bool operator==(const variant& v1, double v2){…}
bool operator==(double v1, const variant& v2){…}
bool operator==(const variant& v1, const string& v2){…}
bool operator==(const string& v1, const variant& v2){…}
bool operator==(const variant& v1, const char* v2){…}
bool operator==(const char* v1, const variant& v2){…}

variant& *=(const variant& v1, double v2){…}
variant& *=(double v2, const variant& v2){…}

我们可以看到,对于每个非variant类型,操作符都成对地重载。通过交换参数的次序,实现不同的操作数类型次序:
10+x; x+10;
至此,variant已经基本完成了。variant可以象内置类型那样使用了。
...全文
1251 58 打赏 收藏 转发到动态 举报
写回复
用AI写文章
58 条回复
切换为时间正序
请发表友善的回复…
发表回复
shezhang999 2008-06-10
  • 打赏
  • 举报
回复
mark
life02 2008-03-24
  • 打赏
  • 举报
回复
非常好的帖子,我定了
收藏
tmhlcwp 2007-07-09
  • 打赏
  • 举报
回复
加剧晕~。~
庄鱼 2007-06-29
  • 打赏
  • 举报
回复
to:Vitin(卫亭)
我不建议楼主作函数重载覆盖的工作,这是有原因的:因为使用C++的人本身要对自己驾驭的数据负责,所以,其会根据需要自由的选择合适的类型,对一些个例化的东西适用相应的替代方案,以达到效率与便捷的统一。
variant这问题实际上本身就存在悖论,在我一个玩Delphi的朋友就很少直接使用该类型做运算,他仅仅拿它作为一个容器来进行数据转换用,用他的话说,用这种变量运算,出了错都不知道错在哪里!举个例子:
var v1,v2,v3 :variant;
v1 := '123';
v2 := '456';
v3 := v1 + v2; ===> v3='579' or v3='123456'!!!
var i1,i2: integer;
var s1,s2:string;
i1 := v1;
i2 := v2;
v3 := i1+i2; ==>v3='579'
s1 := v1;
s2 := v2;
v3 := s1+s2; ===>v3 = '123456'
v3:=s1+v2; ==>v3 = '123456'
v3:=i1+v2; ==>v3 = '579'
用variant变量存在这么多陷阱,有必要将这种陷阱引入到C++里吗?我觉得楼主该完善并不是运算功能,二是完善其容器功能与数据源类型的识别及后续转换功能。也就是说,将类型之间的转化提供便捷与安全的有效手段。
Vitin 2007-06-29
  • 打赏
  • 举报
回复
to FreeFice(庄鱼):
你说的是有道理的。

其实隐式转换或操作符重载等等技术归根到底也只是体现了某种语义的语法形式而已。使用语言的目的是为了实现需要的语义。那么,即使使用其他的语法形式,只要实现了相同的语义,目的就达到了。这里讨论可以用某些技术直接地(隐式地)达到Delphi中variant的相同语义,说的只是一种能力而已,并不是说,在实际使用中必须这样做。这是可行性,不是必然性。
在实际使用中,达到自己期望的语义是最重要的。显式的转换,以及其他的方式,并不比隐式转换或操作符重载低一等的。之所以没有把重点放在这里,是因为LZ希望讨论的是如果使用variant(这样的混合类型),应该怎么做。而不是说variant本身有多好。我们参与讨论,也不是说喜欢使用variant,而是借这个课题将一些技术理解的更深刻、把握的更好。当然,其side effect 是更加了解variant.

至于variant中的语义冲突,我在LZ的下一篇“被误解的C++——优化variant实现 ”的回复中也提到了。当一个类被设计出来时,有些语义可以沿用一些惯例,这比较容易掌握;有些则是它所特有的(如你举的例子应该怎么办)。这里不讨论如果一些特有语义与惯例发生冲突所带来的危害(你举的例子就是一种表现),只说一点:如果存在这些特有语义,那么必须让它的使用者知道这一点,并且帮助使用者学会使用它。因此,variant的设计者会提供相关文档或教程的,当然他们也会保证自恰和行为一致性的。这就是为什么,我强调首先要了解variant的语义,然后才是选择合适的技术手段(以及相应的语法形式)来实现它。

LZ的下一篇“被误解的C++——优化variant实现 ”就是关注类型识别和转换的,当然识别和转换的目的也是为了运算。可以去那边讨论这个主题,呵呵。
snprintf 2007-06-28
  • 打赏
  • 举报
回复
mark
healer_kx 2007-06-28
  • 打赏
  • 举报
回复
以前我是一个泛型的爱好者,看到的都是类型,现在我研究Shellcode呢,看到的都是数字。。。 。。。
Vitin 2007-06-28
  • 打赏
  • 举报
回复
variant& variant::operator=(const char*);
variant& variant::operator=(double);
variant::operator double();

定义上述两个操作符重载和一个隐式转换函数,就可以支持如下的代码:
variant v1;
v1 = "123";
v1 = sqrt(v1);

此外,还可以重载sqrt函数,以支持第二个式子。
例如写一个 variant sqrt(const variant&);
或者为效率起见(避免构造一次variant),可以写两个函数
variant& variant::operator=(double);
double sqrt(const variant&);

theendname 2007-06-28
  • 打赏
  • 举报
回复
先mark
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
又见芙蓉~~~~~

不告诉你有自动类型转换了吗?

自动类型转换到int时,如果发现当前存储的是字符串,就先检查是否能转换为数字——这个简单问题,肯定不是某些人的S级智力所能理解的。
flysky_zhou 2007-06-28
  • 打赏
  • 举报
回复
mark.............

好好的一个帖子......

却有些人在这里口水........

不懂.................
dazhuaye 2007-06-28
  • 打赏
  • 举报
回复
mark
Vitin 2007-06-28
  • 打赏
  • 举报
回复
上面的一些讨论我觉得都有道理。
是的,在C++中要实现一个variant,技术上是可行的,关键是代价和收益的均衡。
使用隐式转换是危险的(要考虑的东西很多,不能遗漏),使用函数重载是繁复的(要实现的东西多,工作量大),还有一些技术则是高深的(比如LZ的下一篇“被误解的C++——优化variant实现 ”中的模板元编程)。毕竟,每一种技术在获得收益时,都是要付出代价的。
因此,如何做技术上的取舍平衡还是很重要的。如对于某些类型,做隐式转换;对于某些操作,做操作符重载;对于某些应用,提供显式的可调用函数;对于某些方面,使用更前沿的技术。如此等等。

此外,我觉得比技术更重要的是variant的语义。技术只是说C++能够实现怎样的语义。但对一个类来说,它需要怎样的语义才是最关键的。如什么时候应该把variant转换成某种类型,什么时候不应该;variant内部的类型应该以怎样的顺序和优先级做转换;有哪些例外,有哪些限制;如此等等。只有先确定了语义(或者说,根据对variant的需求设计出合适的语义),才能用相应的技术实现它。在这里,我觉得可以参考Boost、MFC等的variant实现,体会它们各自的语义;也可以参考Delphi或其他语言对variant的实现(不知道能不能获得,其他语言我不熟悉。有类库及源代码自然最好;如果是内建类型,就要看编译器的实现;得不到第一手的材料,就要研究汇编码——不过,代价很高地说),看看它们又使用了什么语义,以及在C++中怎样实现同样的语义。
其实任何语言都是一样的,她们都通过一定的语法支持一定的语义集合。从这个角度看,语言的区别只是有的语义集合大,有的语义集合小。如果对语言已经掌握了很好,那么问题的关键就是怎样把需求转化为本语言能够支持的语义了。
yuyunliuhen 2007-06-28
  • 打赏
  • 举报
回复
mark
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
这种缺德小人,妓俩不外乎:栽赃诬陷、造谣辱骂、名誉威胁、马甲满天。

我就看着你一样一样用出来。
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
回网友关于“强龙不压地头蛇”的言论:


csdn是谁的地盘?

它是我们真正学习、研究技术的程序员的地盘!

路不平,有人踩。屎当道,有人埋。

本人生平最恨这种缺德小人。既然犯我手里,咱不制住它就没完。
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
呵呵,芙蓉gay,从现在开始,我不再和你谈任何技术问题。我不能侮辱自己的智商,更不能侮辱网友的智商。

至于你这种SB,我见多了。在我已经彻底驳倒你,逼得你看着自己气大的肚皮,写出下面这首歪诗的情况下,我犯的上拿你当菜吗?

这水平,你还号称“从05年以来在CSDN上和我叫板的无一例外都倒了”?被你熏倒了吧。


轰的一声你倒了,
正如你噗的一声鼓起来,
你拍一拍爆裂的肚皮,
垂死中流露出几许无奈;

轰的一声你倒了,
正如你噗的一声鼓起来,
你喷出几个蝌蚪,
把希望寄托给蛤蟆的下一代。
  • 打赏
  • 举报
回复
散分还需要什么胆量吗???
是你自己心惊肉跳了吧。

http://community.csdn.net/Expert/TopicView3.asp?id=5626551
http://community.csdn.net/Expert/TopicView1.asp?id=5624805

还有4个被删了。

没错,是非自有公论,不过不可能是你自造的“业界公认”!
你那些笑料言论,放到哪里也只能是笑料!
你已经出了大丑,你就等着继续丢人吧。
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
绝不会像某疯狗,只敢背后咬人。
shan_ghost 2007-06-28
  • 打赏
  • 举报
回复
另外,关于你的任何链接,我都会在csdn一一贴出。
加载更多回复(38)
第1篇 理解程序设计 第1章 基础知识 1.1 什么是编程 1.1.1 计算机如何工作 1.1.2 内存中的程序是哪里来的 1.1.3 可执行文件的制作 1.1.4 C语言的演化 1.2 怎样用C语言编程 1.2.1 学习C语言编程都需要什么 1.2.2 最简单的C语言程序的基本结构 1.2.3 Dev C++ 1.3 printf()函数初步 1.3.1 简单的一般用法 1.3.2 特殊的字符 1.4 C语言的“字母”和“单词” 1.4.1 C语言的字母 1.4 12C语言的“词” 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第2章 数据类型 2.1 什么是数据类型 2.1.1 “三个世界”理论 2.1.2 问题世界:“万物皆数” 2.1.3 代码世界:书写规则及含义 2.1.4 机器世界里的“机器数” 2.1.5 输出问题 2.1.6 计算2的1到10次幂 2.1.7 代码质量的改进 2.2 让程序记住计算结果——变量 2.2.1 计算机的记忆功能 2.2.2 在代码中实现“记忆 2.3 int类型——总结与补充 2.3.1 计算机表示负整数的几种方法 2.3.2 计算机码制和C语言的关系 2.3.3 暂时不必关心的一些细节 2.3.4 int类型值的范围 2.3.5 int类型常量在代码中的其他写法 2.3.6 Dev C++中int类型的机器数 2.4 对数据类型的进一步讨论 2.4.1 int数据类型的运算 2.4.2 数学公式与数据类型 2.4.3 数据类型——代码与编译器的约定 2.5 莫名其妙的“整型 2.5.1 unsignedint类型 2.5.2 long、short关键字描述的整数类型 2.5.3 没有常量的char类型 2.5.4 其他 2.6 浮点类型 2.6.1 double类型常量的代码书写规则 2.6.2 浮点类型数据存储模型 2.6.3 浮点类型的一些特性 2.6.4 浮点类型的运算 2.6.5 浮点类型的输出及其他 2.7 数据类型与算法 2.7.1 错误的数据类型 217.2 所谓算法 2.7.3 一个技巧 2.7.4 更高效率的写法 2.8 算法的特性 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第3章 运算符、表达式及语句 3.1 C的“动词”及“动词”的“宾语” 3.2 表达式——C语言的“词组 3.2.1 初等表达式 3.2.2 被误解的“() 3.2.3 带运算符的表达式 3.2.4 不像表达式的表达式 3.2.5 表达式:专业与副业 3.2.6 赋值运算符左侧的标识符称为左值 3.2.7 函数调用是表达式不是语句 3.3 谁是谁的谁 3.3.1 流行的谬误:优先级决定运算次序 3.3.2 “左结合性”是运算对象先与左面的运算符相结合吗 3.3.3 运算符、表达式小结 3.4 右值的类型转换 3.4.1 明确写出的显式转换——cast运算 3.4.2 cast运算的规则 3.4.3 赋值中的转换 3.4.4 1+1.0=? 3.4.5 算术转换:早已废弃的规则和依然有效的规则 3.5 语句的概念 3.5.1 关于语句的闲话 3.5.2 空语句有两种 3.5.3 表达式语句 3.5.4 顺序结构 3.5.5 复合语句 3.6 例题 3.6.1 简单的类型转换 3.6.2 最基础的算法——交换变量的值 3.6.3 编程不是列公式 3.7 算法和数据结构初窥 3.8 在程序运行时提供数据 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第4章 选择语句 4.1 关系运算 4.1.1 “<”的数学含义及代码含义 4.1.2 4种关系运算符 4.1.3 常见误区及与常识不符的结果 4.2 if语句 4.2.1 语法格式及含义 4.2.2 例题 4.2.3 ()内的表达式 4.2.4 ()后面的语句 4.3 判等运算 4.4 表达复杂的条件 4.5 if else语句 4.6 鸡肋——Bool类型(C99) 4.7 判断三角形种类 4.8 显得很有学问的运算符 4.9 大师如是说goto 4.10 给程序更多选项——Switch语句 4.10.1 switch语句的一种应用形式 4.10.2 switch语句中的break语句 4.11 程序开发的过程 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第5章 从循环到穷举 5.1 造句:当就 5.1.1 语法要素 5.1.2 猴子吃桃问题更简洁的写法 …… 第2篇 结构化程序设计与简单的数据结构 第6章 最复杂的去处符——“()” 第7章 作为类型说明符和去处符的“[]” 第8章 结构体、共用体与位运算 第9章 指针 第10章 字符串、字符数组及指向字符的指针 第3篇 复杂的数据结构、算法及其他话题 第11章 复杂的数据类型与算法 第12章 程序的输入与输出 第13章 程序组织与编译预处理 第14章 标准库简介 附录 参考文献
► 数据结构--序言数据结构--序言 在可视化化程序设计的今天,借助于集成开发环境可以很快地生成程序,程序设计不再是计算机专业人员的专利。很多人认为,只要掌握几种开发工具就可以成为编程高手,其实,这是一种误解。要想成为一个专业的开发人员,至少需要以下三个条件: 能够熟练地选择和设计各种数据结构和算法。 至少要能够熟练地掌握一门程序设计语言。 熟知所涉及的相关应用领域的知识。 其中,后两个条件比较容易实现,而第一个条件则需要花相当的时间和精力才能够达到,它是区分一个程序设计人员水平高低的一个重要标志,数据结构贯穿程序设计的始终,缺乏数据结构和算法的深厚功底,很难设计出高水平的具有专业水准的应用程序。曾经有一本经典计算机专业书籍叫做《数据结构+算法=程序》,也说明了数据结构和算法的重要性。 《数据结构》是计算机科学与工程的基础研究之一,掌握该领域的知识对于我们进一步进行高效率的计算机程序开发非常重要。无论在中国还是在美国,《数据结构》一直是大学的计算机专业重要的专业基础课。例如,在著名的美国的加州大学伯克利分校(著名的BSD Unix的发源地,很多Unix操作系统由它派生而来或带有它的痕迹——例如FreeBSD、Sun公司的Solaris、IBM的AIX),就用一个学期开设《数据结构和算法》课程(在这之前,用一个学期开设《C++程序设计》课程)。 现行的中学相关的计算机教程或者是关于怎样使用Windows操作系统及其工具、或者是有关办公软件的使用,或者是打字教程。计算机对他们始终有一种神秘感,也许是理论导向吧,因为不可能每个人将来都成为计算机专业人员。 作为一个中学生,在学完C/C++以后,关键的问题是怎样熟练地应用和巩固。本网站希望能够结合《数据结构》和相关的数、理、化知识来巩固C/C++。其实《数据结构》并不难。可以说,数据结构贯穿于我们的数学课程之中,只是思考问题方法的不同。在大学的《数据结构》教程中,很多生僻的词语、晦涩难懂的语句,连大学生就感到望而生畏。本网站将集合小学和中学的数学、物理、化学教材,深入浅出地讲解这门课程。希望不但能够对学习电脑有所帮助,更希望能够对数理化的学习起到一个促进作用。 在学习《数据结构》之前,要求学生有C/C++基础。可以这样说,C/C++是其他程序设计语言的基础。掌握了C/C++,学习其他语言就会易如反掌。例如,微软的MFC类库基于C++;ATL基于C++中的模板类;Java语言基于C++思想,其编程风格与C++差别很小;C++ Builder又是基于C++;Delphi中的有关对象的概念与C++中的对象几乎完全一致。C++相比其他语言具有与计算机硬件集合紧密、代码效率高,这是Java语言和其他高级语言所无法比拟的。这样,C/C++对于学习计算机系统结构有很大的好处。

64,649

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧