从 Java到C++ — 对比Java与C++编程的不同
英文原文: http://www.horstmann.com/ccj2/ccjapp3.html
引用
刚开始学编程学basic,后来费了九牛二虎之力学了c++,简直是境界的蜕变啊。工作了用的却是Delphi。后来觉得java的工作比较好找,速成了java。当初费了九牛二虎之力学习的c++皮毛如今已经荡然无存了。更糟糕的是有时还会因java的概念而混淆了c++的概念,特搜此文以鉴。
学习怎样从一种编程语言转移到另一种编程语言是今日的程序员必须面对的现实,还好,C++和Java有很多共同的特点,所以从Java转到C++就容易许多。C++比Java复杂很多,本文并没打算涵盖所有C++的功能。但是如果你能够熟练掌握本文中的所有知识点,也足够有效的使用C++了。
这里我们只讲Java与C++的不同之处。像流程控制(if, while, for)这些在C++与Java中完全一样的内容这里就不讲了。
本文是基于ANSI C++标准的,一些老的C++ 编译器可能不支持这里讲到的一些重要功能。要使用这些编译器,你需要更多学习C++中从C继承来的部分。那些都超出了本文的范畴,也就不介绍了。
1. 数据类型和变量
C++ 中的变量类型与Java很相似。像Java一样,C++ 有int 和 double 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上,例如运行DOS 或Windows 3.x的PC机上,int 是双字节(2-byte)的,取值范围比Java的4-byte的int 要小很多。在这些机器上,如果 int 不够用的话,你需要使用长整型long。
C++ 有 short 和 unsigned 类型来更有效的存储数字。(我认为所谓有效是指更高的空间利用率。) 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。
在C++中布尔型用 bool 表示,而不像在Java中用boolean。
C++ 中字符串类型用 string 表示。它与Java中的 String 类型非常相似,但是,还是要逐一以下几点不同之处:
1. C++ 字符串存储ASCII 码字符,而不是标准码Unicode 字符
2. C++ 字符串是可以被修改的,而Java字符串的内容是不可修改的(immutable)。
3. 取子字符串的操作在 C++ 中叫做 substr,这个命令s.substr(i, n) 从字符串s中取得从位置 i 开始长度为n的子字符串。
4. 在C++中,你只能够将字符串与其它字符串对象相串联(concatenate),而不能够与任意的对象相串联。
5. C++中可以直接使用关系操作符 ==、 !=、 <、 <=、 >、 >= 来进行字符串比较,其中后面四个操作符是按字母顺序进行比较的。 这比Java中使用函数equals和compareTo来比较要方便很多。
2. 变量和常量
在C++中,本地变量的定义看起来与Java中相同,例如:
int n = 5;
实际上这正是C++和Java的一个重要不同之处。C++编译器不对本地变量进行初始化检验,所以在C++中很容易忘记初始化一个变量,这种情况下,变量的值该变量所占内存区域中刚好当前存在随机值。这显然是很容易产生程序出错的地方。
与Java一样, C++中类可以有数据域和静态变量。不同的是,C++中变量可以在函数甚至是类的外面定义,这些所谓的全局变量可以在程序的任何函数中被访问,因而不易被很好的管理。所C++中应该尽量避免使用全局变量。
在C++中,常量可以在任何地方被定义(记得在Java中,常量必须是类的静态数据static data)。 C++ 使用关键字 const 来定义常量,而Java中是 final。例如:
const int DAYS_PER_YEAR = 365;
3. 类
C++ 中对类的定义与Java有些不同,这里是一个例子:一个C++ 版本的 Point 类:
class Point /* C++ */
{
public:
Point();
Point(double xval, double yval);
void move(double dx, double dy);
double getX() const;
double getY() const;
private:
double x;
double y;
};
这里几点重要的不同是:
1. C++的类定义中分为公共和私有部分,分别以关键字 public 和 private开始。而在 Java中,每一个元素都必须标明 public 或 private。
2. C++中类的定义只包含函数的声明,真正的实现另外单独列出。
3. 访问函数(accessor methods)标有关键字 const ,表明这个函数不会改变本对象的元素值。
4. 类定义的结尾处有分号
类中函数的实现跟在类的定义之后。因为函数是在类外面定义的,所以每一个函数的名字前面要加类名称作为前缀,并使用操作符双冒号::来分割类的名称和函数的名称。不改变隐含参数值(即当前对象的值)的访问函数用 const标明。如下所示是上面类定义中的函数的实现:
Point::Point() { x = 0; y = 0; }
void Point::move(double dx, double dy)
{
x = x + dx;
y = y + dy;
}
double Point::getX() const
{
return x;
}
4. 对象
Java 与 C++ 最主要的不同在于对象变量的使用。在 C++中,对象变量存储的是真正的对象的值,而不是对象引用(reference)。注意在C++中构造一个对象的时候是不使用关键字new的,只需要在变量的名字后面直接赋予构造函数的参数就可以了,例如:
Point p(1, 2); /* 构造对象 p */
如果不跟参数赋值,则使用默认构造函数,例如:
Time now; /* 默认使用构造函数 Time::Time() */
这一点与Java很不同。在Java中,这个命令仅仅生成一个没有初始化的reference,而在C++中,它生成一个实际的对象。
当一个对象被赋给另一个对象变量的时候,实际的值将被拷贝。而在Java中,拷贝一个对象变量只不过是建立了另外一个指向对象的 reference。拷贝一个C++的对象就像在Java中调用clone这个函数一样,而修改拷贝的值不会改变原对象的值。例如:
Point q = p; /* 拷贝p到q */
q.move(1, 1); /* 移动q而p不动,即q的值变了,而p的不变*/
多数情况下,C++中这种对象直接对值操作的特性使用起来很方便,但是也有些时候不尽如人意:
1. 当需要一个函数中修改一个对象的值,必须记住要使用按引用调用call by reference (参见下面函数部分)
2. 两个对象变量不能指向同一个对象实体。如果你要在C++中实现这种效果,必须使用指针pointer(参见下面指针部分)
3. 一个对象变量只能存储一种特定的类型的值,如果你想要使用一个变量来存储不同子类的对象的值(多态ploymorphism),则需要使用指针。
4. 如果你想在C++中使用一个变量来或者指向null或者指向一个实际的对象,则需要使用指针
5. 函数
在Java中,每一个函数必须或者是对象函数(instance method),或者是静态函数(static function)或称类函数。C++同样支持对象函数和静态函数(类函数),但同时C++也允许定义不属于任何类的函数,这些函数叫做全局函数(global functions)。
特别的是,每一个C++ 程序都从一个叫做 main的全局函数开始执行:
int main()
{ . . .
}
还有另外一个格式的main函数可以用来捕捉命令行参数,类似于Java的main函数,但是它要求关于C格式的数组和字符串的知识,这里就不介绍了。
按照习惯,通常如果程序执行成功, main 函数返回0,否则返回非零整数。
同Java一样,函数参数是通过值传递的(passed by value)。在Java中,函数无论如何都是可以修改对象的值的。然而在C++中,因为对象直接存储的是实际的值,而不是指向值的reference,也就是说传入函数的是一个实际值的拷贝,因此也就无法修改原来对象的值。
所以,C++ 有两种参数传递机制,同Java一样的按值调用(call by value) ,以及按地址调用(call by reference)。当一个参数是按reference传递时,函数可以修改其原始值。Call by reference 的参数前面有一个地址号 & 跟在参数类型的后面,例如:
void raiseSalary(Employee& e, double by)
{ . . .
}
下面是一个典型的利用call by reference的函数,在Java中是无法实现这样的功能的。
void swap(int& a, int& b)
{ int temp = a;
a = b;
b = temp;
}
如果使用 swap(x, y)来调用这个函数,则reference参数 a 和 b 指向原实际参数x 和 y的位置,而不是它们的值的拷贝,因此这个函数可以实现实际交换这两个参数的值。
在 C++中,每当需要实现修改原参数的值时你就可以使用按地址调用 call by reference 。
6. 向量Vector
C++ 的向量结构结合了Java中数组和向量两者的优点。一个C++ 的向量可以方便的被访问,其容量又可以动态的增长。如果 T 是任意类型,则 vector<T> 是一个元素为 T 类型的动态数组。下面的语句
vector<int> a;
产生一个初始为空的向量。而语句
vector<int> a(100);
生成一个初始有100个元素的向量。你可以使用push_back 函数来添加元素:
a.push_back(n);
调用 a.pop_back() 从a中取出最后一个元素(操作后这个元素被从a中删掉), 使用函数size 可以得到当前a中的元素个数。
你还可以通过我们熟悉的 [] 操作符来访问向量中元素,例如:
for (i = 0; i < a.size(); i++) {
sum = sum + a[i];
}
同Java中一样,数组索引必须为 0 和 a.size() - 1之间的值。但是与Java不同的是,C++中没有 runtime的索引号合法性检验。试图访问非法的索引位置可能造成非常严重的出错。
就像所有其它 C++ 对象一样,向量也是值。如果你将一个向量赋值给另外一个向量变量,所有的元素都会被拷贝过去。
vector<int> b = a; /* 所有的元素都被拷贝了 */
对比Java中的情况,在Java中,一个数组变量是一个指向数组的reference。拷贝这个变量仅仅产生另外一个指向同一数组的 reference,而不会拷贝每一个元素的值。
正因如此,如果一个C++函数要实现修改向量的值,必须使用reference参数:
void sort(vector<int>& a)
{ . . .
}
7. 输入和输出
在C++中,标准的输入输出流用对象 cin 和 cout 表示。我们使用 << 操作符写输出,例如:
cout << "Hello, World!";
也可以连着输出多项内容,例如:
cout << "The answer is " << x << "\n";
我们使用 >> 操作符来读入一个数字或单词,例如:
double x;
cout << "Please enter x: ";
cin >> x;
string fname;
cout << "Please enter your first name: ";
cin >> fname;
函数getline 可以读入整行的输入,例如:
string inputLine;
getline(cin, inputLine);
如果到达输入的结尾,或者一个数字无法被正确的读入,这个流对象会被设置为 failed 状态,我们可以使用函数 fail 来检验这个状态,例如:
int n;
cin >> n;
if (cin.fail()) cout << "Bad input";
一旦一个流的状态被设为failed,我们是很难重置它的状态的,所以如果你的程序需要处理错误输入的情况,应该使用函数 getline 然后人工处理得到的输入数据。
8. 指针pointer
我们已经知道在C++中,对象变量直接存储的是对象的值。这是与Java不同的,在Java中对象变量存储的是一个地址,该地址指向对象值实际存储的地方。有时在C++中也需要实现这样的布置,这就用到了指针pointer。在 C++中,一个指向对象的变量叫做指针。如果T是一种数据类型,则 T* 是指向这种数据类型的指针。 这里重点介绍C++与Java的不同,要详细了解C++中指针的使用,请参考本站另一篇文章“C++中Reference与指针(Pointer)的使用对比”。
就像 Java中一样,一个指针变量可以被初始化为空值 NULL,另外一个指针变量的值,或者一个调用new生成的新对象:
Employee* p = NULL;
Employee* q = new Employee("Hacker, Harry", 35000);
Employee* r = q;
实际上在C++中还有第四种可能,那就是指针可以被初始化为另外一个对象的地址,这需要使用地址操作符 & :
Employee boss("Morris, Melinda", 83000);
Employee* s = &boss;
这实际上并不是什么好主意。保险的做法还是应该直接让指针指向使用 new生成的新对象。
到目前为止,C++ 指针看起来非常像 Java 的对象变量。然而,这里有一个很重要的语法的不同。我们必须使用星号操作符 * 来访问指针指向的对象。如果 p 是一个指向Employee对象的指针,则 *p 才代表了这个对象:
Employee* p = . . .;
Employee boss = *p;
当我们需要执行对象的函数或访问对象的一个数据域时,也需要使用 *p :
(*p).setSalary(91000);
*p外面的括号是必需的,因为 . 操作符比 * 操作符有更高的优先级。C的设计者觉得这种写法很难看,所以他们提供了另外一种替代的写法,使用 -> 操作符来实现 * 和 . 操作符的组合功能。表达式
p->setSalary(91000);
可以调用对象*p的函数 setSalary 。你可以简单的记住 . 操作符是在对象上使用的,-> 操作符是在指针上使用的。
如果你不初始化一个指针,或者如果一个指针为空值 NULL 或指向的对象不再存在,则在它上面使用 * 或 -> 操作符就会出错。 不幸的是 C++ runtime 系统并不检查这个出错。如果你范了这个错误,你的程序可能会行为古怪或死机。
而在Java中,这些错误是不会发生的。所有的reference都必须初始化,所有的对象只要仍有reference指向它就不会被从内存中清除,因此你也不会有一个指向已被删除的对象的reference。Java的runtime 系统会检查reference是否为空,并在遇到空指针时抛出一个null pointer的例外(exception)。
C++ 和 Java还有一个显著的不同,就是 Java 有垃圾回收功能,能够自动回收被废弃的对象。而在C++中,需要程序员自己管理内存分配回收。
C++中当对象变量超出范围时可以自动被回收。但是使用new生成的对象必须用delete操作符手动删除,例如:
Employee* p = new Employee("Hacker, Harry", 38000);
. . .
delete p; /* 不在需要这个对象 */
如果你忘记删除一个对象,那么你的程序有可能最终用光所有内存。这就是我们常说的内存泄漏 (memory leak)。更重要的是,如果你如果删除了一个对象,然后又继续使用它,你可能覆盖不属于你的数据。如果你刚巧覆盖了用于处理内存回收的数据域,那么内存分配机制就可能运转失常而造成更严重的错误,而且很难诊断和修复。因此,在C++中最好尽量少用指针。
9. 继承
C++和Java中继承的基本语法是很相似的。在C++中,使用 : public 代替Java中的extends 来表示继承关系 。 (C++ 也支持私有继承的概念,但是不太有用。)
默认情况下,C++中的函数不是动态绑定的。如果你需要某个函数实现动态绑定,需要使用virtual声明它为虚函数,例如:
class Manager : public Employee
{
public:
Manager(string name, double salary, string dept);
virtual void print() const;
private:
string department;
};
同Java一样,构造函数中调用父类的构造函数有特殊的语法。 Java使用关键字 super。C++中必须在子类的构造函数体外调用父类的构造函数。下面是一个例子:
Manager::Manager(string name, double salary, string dept)
: Employee(name, salary) /* 调用父类的构造函数 */
{ department = dept;
}
Java 中在子类函数中调用父类的函数时也使用关键字 super 。而在C++中是使用父类的名称加上操作符 ::表示,例如:
void Manager::print() const
{ Employee::print(); /* 调用父类的函数 */
cout << department << "\n";
}
一个 C++ 对象变量只能存储特定类型的对象值。要想在C++中实现多态(polymorphism),必须使用指针。一个 T* 指针可以指向类型为 T 或 T 的任意子类的对象,例如:
Employee* e = new Manager("Morris, Melinda", 83000, "Finance");
你可以将父类和不同子类的对象混合收集到一个元素均为指针的向量中,然后调用动态绑定的函数,如下所示:
vector<Employee*> staff;
. . .
for (i = 0; i < staff.size(); i++)
staff[i]->print();
分享到:
相关推荐
C++与Java在面向对象特性上的比较: 1. 封装:两者都支持封装,即隐藏对象的内部实现细节,通过公有接口暴露必要的功能。C++通过访问修饰符(public, private, protected)来控制成员的可见性,而Java则有类似的机制...
本文将深入探讨从C++编程转向Java编程的相关知识点,帮助程序员理解这两个语言的区别、相似性以及如何有效地进行过渡。 首先,C++和Java都是面向对象的编程语言,但它们的设计哲学和应用领域有所不同。C++起源于...
Delphi 编程技术与java,php,net.C++编程语言的对比.docx.zipDelphi 编程技术与java,php,net.C++编程语言的对比.docx.zipDelphi 编程技术与java,php,net.C++编程语言的对比.docx.zipDelphi 编程技术与java,php,...
本书《面向对象编程:C++与Java比较教程》详细比较和对比了当今最流行的两种编程语言C++和Java,旨在教授互联网时代至关重要的技能和面向对象编程技术。作者Avinash C. Kak凭借其成功教授的课程,撰写了一本全面的...
C++语言的数据类型繁多,从简单的整型、字符型到复杂的浮点型和逻辑型,每种数据类型还能进一步细分为双精度、单精度等具体形式。指针类型数据在C++中非常常见,通过指针变量能够直接操作内存地址。 C++语言的应用...
总的来说,Java转C++代码工具J2C为开发者提供了一种将Java项目迁移到C++环境的可能性,但这个过程并不简单,需要对两种语言都有深入的理解。转换工具只是第一步,后续的工作包括代码审查、调试和优化,才能确保转换...
本教程对比了两种广泛应用的面向对象语言——C++和Java,帮助读者深入理解两者之间的异同。 C++是Bjarne Stroustrup于1983年创建的,它是C语言的扩展,增加了类、模板、异常处理等面向对象特性。C++强调性能和灵活...
在IT行业中,跨语言通信是常见的需求,尤其是在Java和C++这样的不同编程环境中。本话题主要探讨如何使用Java调用由C++实现的Web服务(Webservice)。在给出的描述中,提到了通过WSDL(Web Services Description ...
C++和Java是两种不同的编程语言,每种都有其独特的语法和特性。C++是一种静态类型、编译式的、通用的、大小写敏感的、不仅支持过程化编程,也支持面向对象编程的语言。而Java是一种面向对象的、跨平台的、动态类型的...
此文档旨在全面对比Java和C++,整合了网络上的众多资源,旨在帮助读者理解和解决实际编程中遇到的问题。尽管它可能不如专门的书籍详细,但仍提供了丰富的对比信息。 (二) 个人学习感受 作者从自身经验出发,指出...
JAVA 和 C++是两种广泛应用的编程语言,两者都是面向对象的,但在设计理念和实现机制上存在显著差异。本文将深入探讨这些区别。 首先,指针是C++中的核心概念,但在JAVA中则被严格限制。JAVA不直接允许程序员操控...
在探讨"C++与Java混合编程"这一主题时,我们首先需要理解这两个语言的基本特性以及它们在混合编程场景下的应用方式。C++是一种静态类型的、通用的、编译型的编程语言,它支持过程化编程、面向对象编程和泛型编程等...
本主题聚焦于"C++与Java混合编程实例",这涉及到如何在C++和Java这两种不同语言之间进行交互,以实现代码复用、性能优化或是利用两者的优势。 C++是一种静态类型的、编译式的、通用的、大小写敏感的、不仅支持过程...
"C++转换JAVA工具" 提供了一种解决方案,使得开发者可以从C++代码无缝过渡到Java代码,或者将Java代码转换为C++,以适应不同的开发需求和环境。这种工具的主要目标是提高开发效率,降低维护成本,以及实现平台间的...
Qt提供Android应用中写Java代码的功能。这就像人们用Android Studio(或者Eclipse等)开发Android应用程序时可用写C++程序一样。默认情况下,Qt只能用Qt提供的模块(大多数情况下已经够用了)写C++程序。但是,在...
Java和C++之间的Socket...总之,Java和C++通过Socket通信涉及到网络编程的基本概念、数据类型转换、错误处理以及跨平台兼容性等多个方面。理解并熟练掌握这些知识点,可以有效地实现两个不同语言的程序间的高效通信。
根据《C++编程思想》一书的观点,C++与C之间的效率差异往往在正负5%之间,因此,在大多数情况下,C++完全可以替代C语言(特别是在单片机等领域,但需要注意对硬件的直接操作或使用C语言的情况)。 Java是一种广泛...