Java开发者使用C++写程序踩的坑
笔者是一个很矛盾的人。平时用Java。但是一开始学习的时候学的是汇编语言,而且对C语言也很熟悉。为什么不学C++呢?是因为我可以完全用Java的编码规范去写C++。因此我不需要了解更多的诸如C++的命名空间,操作符重构,友元函数,多继承这些更复杂的特性。
虽然C++介于Java和C之间,我并不需要学C++(开发用Java,脚本用ruby,python,JavaScript)、但是作为一个计算机从业人员。不了解C++有点说不过去。
首先我先总结这次我学习C++的特性:(参考资料:菜鸟教程)
一、using namespace std
第一个让我疑惑的是using namespace std;
话说回来这个是什么意思?
当我对比了下Java文件的命名规范(类名=文件名.java),我就懂了。
使用Java开发的人都明白一个道理、当你import 一个类的时候,Java 的包名.类名的文件定义,而且一切皆对象,会使你不会遇到诸如C++名称空间的这种错误。
例如你在头文件中定义一个函数,这个函数如果被别人又定义了,在编译时会报错的。因此C++语言加了个命名空间的语言特性。
有的同学可能问了,如果函数名重复了话,可以重构啊,。。。但是要是参数都一样呢?
所以C++会用这种方式,定义了一个解决问题的方法。
下面是别人总结的。
命名空间(namespace)是一种描述逻辑分组的机制,可以将按某些标准在逻辑上属于同一个集团的声明放在同一个命名空间中。 原来C++标识符的作用域分成三级:代码块({……},如复合语句和函数体)、类和全局。现在,在其中的类和全局之间,标准C++又添加了命名空间这一个作用域级别。 命名空间可以是全局的,也可以位于另一个命名空间之中,但是不能位于类和代码块中。所以,在命名空间中声明的名称(标识符),默认具有外部链接特性(除非它引用了常量)。 在所有命名空间之外,还存在一个全局命名空间,它对应于文件级的声明域。因此,在命名空间机制中,原来的全局变量,现在被认为位于全局命名空间中。 标准C++库(不包括标准C库)中所包含的所有内容(包括常量、变量、结构、类和函数等)都被定义在命名空间std(standard标准)中了。
std又是什么呢? std是头文件<iostream>中的一个命名空间。包含了cin cout endl等函数。
using 指的是用这个命名空间的函数。
具体使用如下:
#include <iostream> using namespace std; // first name space namespace first_space{ void func(){ cout << "Inside first_space" << endl; } } // second name space namespace second_space{ void func(){ cout << "Inside second_space" << endl; } } int main () { // Calls function from first name space. first_space::func(); // Calls function from second name space. second_space::func(); return 0; }
二、类Class的定义
C++除了支持C语言中的结构体struct外,还支持class。
C语言中的struct里面不能放方法、但是C++可以。
很多时候class和struct的差别其实不大。好像在默认访问权限和继承方式有所不同,其余的我也没看到什么新特征。
不过C++与Java的区别在于C++可以在定义类之后,在其他地方定义方法。这个在Java里面是不行的。Java不可以这样。(别跟我说重载)
具体的实现如下:
#include <iostream> using namespace std; class Box { public: double length; // 长度 double breadth; // 宽度 double height; // 高度 }; int main( ) { Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box double volume = 0.0; // 用于存储体积 // box 1 详述 Box1.height = 5.0; Box1.length = 6.0; Box1.breadth = 7.0; // box 2 详述 Box2.height = 10.0; Box2.length = 12.0; Box2.breadth = 13.0; // box 1 的体积 volume = Box1.height * Box1.length * Box1.breadth; cout << "Box1 的体积:" << volume <<endl; // box 2 的体积 volume = Box2.height * Box2.length * Box2.breadth; cout << "Box2 的体积:" << volume <<endl; return 0; }
同时,类的继承和Java也有区别,他这个居然直接+:就好了,,,呵呵呵
在派生类(继承类)中的使用如下
#include <iostream> using namespace std; // 基类 class Shape { public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // 派生类 class Rectangle: public Shape { public: int getArea() { return (width * height); } }; int main(void) { Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; return 0; }
还有关于多态的问题,和Java差不多。这个我没有特别细究。应该没什么区别吧。。
多态的示例程序如下:
#include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // 程序的主函数 int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0; }
Java的抽象函数和接口在C++里面也有体现
对应的叫虚函数。
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0; };
然后子类继承后,就顺利的重载。
接口的话,就是完完全全的那种纯虚函数。
像这种的
class Box { public: // 纯虚函数 virtual double getVolume() = 0; private: double length; // 长度 double breadth; // 宽度 double height; // 高度 };
三、模板
在Java里面没有模板的概念。那个叫泛型。在集合类,容器类等等应用十分广泛。但是C++这里不知道为啥又叫模板又叫泛型。。管他呢反正都是一个东西。
定义的如下
template <class type> ret-type func-name(parameter list) { // 函数的主体 }
具体的实现如下
#include <iostream> #include <string> using namespace std; template <typename T> inline T const& Max (T const& a, T const& b) { return a < b ? b:a; } int main () { int i = 39; int j = 20; cout << "Max(i, j): " << Max(i, j) << endl; double f1 = 13.5; double f2 = 20.7; cout << "Max(f1, f2): " << Max(f1, f2) << endl; string s1 = "Hello"; string s2 = "World"; cout << "Max(s1, s2): " << Max(s1, s2) << endl; return 0; }
这样就实现了类似于Java中的集合类的方法。
四、操作符的重载
重载这个我是真感觉C++太自由了。
也许C++本就不是为工程而设计的。
而是为一群真正的计算机爱好者定制的。
Java除了String 貌似别的都不行。哈哈
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
像这样:
#include <iostream> using namespace std; class Box { public: double getVolume(void) { return length * breadth * height; } void setLength( double len ) { length = len; } void setBreadth( double bre ) { breadth = bre; } void setHeight( double hei ) { height = hei; } // 重载 + 运算符,用于把两个 Box 对象相加 Box operator+(const Box& b) { Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度 }; // 程序的主函数 int main( ) { Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box Box Box3; // 声明 Box3,类型为 Box double volume = 0.0; // 把体积存储在该变量中 // Box1 详述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 详述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // Box1 的体积 volume = Box1.getVolume(); cout << "Volume of Box1 : " << volume <<endl; // Box2 的体积 volume = Box2.getVolume(); cout << "Volume of Box2 : " << volume <<endl; // 把两个对象相加,得到 Box3 Box3 = Box1 + Box2; // Box3 的体积 volume = Box3.getVolume(); cout << "Volume of Box3 : " << volume <<endl; return 0; }
总结
我真心觉得很多学生说C++难不是因为C++真的难,而是这种高自由的语法,很难让一个初学者学生去掌握。对于学生来说最好先规范计算机的思维然后再学习C++,同时推荐大家去学Java。找工作简单,语言简单。
而要说精通C++我觉得至少要把GCC 或者Clang 都学个精通才算精通吧。毕竟语言的使用只是语言的一部分。