C#者重建C++之路 - 基本类型辨析
搞了几年的C#,现在又需要重新找回C++,这一份是学习的笔记(以C++ Primer第四版为主,干货为主,代码甚少),也算是细节方面的辨析总结吧。当然如果有同学觉得不对的,咱们可以讨论一下,互相学习,互相提高嘛。
1.C#与C++程序的入口都是main函数,不同的是C#可以返回void,但是C++必须返回int值。而且所有C#的代码都必须放到类中(完全体的面向对象),不存在全局函数/变量。
2.C#定义类型的时候,全部代码放到一个cs文件中(也可以使用partial定义多个,编译以后是一个,姑且就认为是一个类型吧)。通常C++的源代码分为2块,一块放声明(通常为.h文件,不同的编译器不一样),一块放实现(通常为.cpp文件,不同的编译器不一样)。
3.C#的基本输出是通过Console类实现的(C#中system.dll,system.core.dll包含了大部分的基本类型);C++是通过标准输入输出库iostream实现(cin, cout, cerr, clog,处于命名空间std下,需要通过::调用;C#中也有这个符号,作用类似)。
Console.WriteLine("Hello world!");
Console.ReadLine();
//C++
using namespace std;
string s;
cin>>s;
cout<<s<<endl;
4.C#使用“Add Reference”引用类库后,再使用using语句可以简化类型引用;C++是通过预编译指令#include实现(#include ""形式与#include <>形式差别在于,前者会先查找程序目录然后是标准库,适合于引用用户自定义的类型,后者直接查找标准库,省时省力)引用类库的。此外C++中也有using语句,它的作用是简化类库中类型的引用,如加上"using std::cin;"或者"using namespace std;"以后,在程序中就可以直接使用"cin"了。这么说来“C#中的using”基本等于“C++中的using”。
5.C#与C++的注释是一脉相承的,没区别。
6.C#的基本内置类型长度固定,不会随着机器的CPU位数改变而改变,如int是32位的,则不管在任何处理器中,它始终都是32位的;C++中的内置类型只定义了最小的存储长度,不同的机器,同一个类型能表达的范围可能不同。
7.C#中使用"@"后字符串可以写在多行。C++中使用"\"后连代码都可以拆开写在多行。此外C++中多个仅由空格、制表符或换行符相隔的字符串会连接成一个字符串。
8.C#已经扩充成半动态的语言(dynamic和DLR在4.0中加入);C++是强类型静态语言,编译时检查错误。
9.C#中引用类型都是需要显式调用构造函数实例化的(使用new关键字,在托管堆中分配,当然了string是经过特殊处理的);C++中如果类型是有默认构造器的,如果不显式调用构造函数,则会隐式调用默认的构造函数(C++中也有new关键字,作用是分配空间,需要使用delete去释放)。其实C#的值类型也差不多是这样的,只不过编译器会强烈建议你初始化基本类型后再去使用。
MyClass m1 = new MyClass();
//C++
string s;//调用默认构造函数得到空字符串
string s1("aaa");
string s2(10,'c');
10.C#中定义了类型后会分配线程栈空间并初始化这块空间(值类型直接初始化为默认值;引用类型初始化为null,不过只有用new实例化后才会在托管堆中分配空间)。C++中分为两种,一个为声明(用extern标识,当然声明的变量可以去直接初始化,如果不直接初始化,则不会分配空间;通常用于多个文件访问同一个变量的情况),一个为定义(直接定义类型与名称);C++中变量可以声明多次,但是只能定义一次。其实C#中也有extern关键字,不过它一般是配合DllImport Attribute来声明某方法是在外部组件中实现的;或者是定义外部别名(extern alias)的。
11.C#与C++都推荐使用const定义多次重用的常数,易于维护易于阅读。
当然在C#中,const常量推荐是应该用static readonly替换的,原因是const是编译时常量,编译完所有引用const常量的地方全部会替换成const代表的值,这样当const值改变而不去重新编译所有引用的工程的话,值就出现不一致的情况了;而static readonly作用与const类似,但是它是运行时常量,也就是说运行的时候才会查找这个常量,所以没有const编译的这个不一致问题。除此之外,由于static readonly常量是运行时常量,它不仅仅可以赋一些编译时值,还可以赋一些运行时获取的值。细细体会下面两个赋值的情况:
private const string Path="c:\\File.txt";
private static readonly string Path1 = System.Windows.Forms.Application.StartupPath + “File.txt”;
其实我觉得const与static readonly才是具有可比性的一对。至于老生常谈的const与readonly,我觉得不具有什么可比性;但是为了凑合一下,还是说一下大致的区别吧:
- const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。
- const 字段是编译时常数,而 readonly 字段可用于运行时常数。
- const 默认就是静态的,而 readonly 如果设置成静态的就必须显示声明。
- const 对于引用类型的常数,可能的值只能是 string 和 null。readonly可以是任何类型。
C++中const常量可以使用extern修饰(不修饰的话,只有定义的文件才可使用,即使是定义在全局作用域中也不行,这是与变量不同的地方,变量默认是extern的),这样其它文件也可以使用。此外,由于C++使用的是类似C#中的值类型的机制,所以可以使用变量名给常量赋值,常量的值就是这个时候变量的值,但是赋值后不能去修改这个常量的值。
12.C#中引用基本是变成了引用类型,而且是类型安全的。C++中的引用(变量名前加"&"前缀)实际上是变量的别名,也就是变量的地址名,定义的时候就需要把变量名赋值给引用。由于C++使用的是类似C#中的值类型的机制,如果不使用引用,两个变量的值不会互相影响。const引用可以可以指向const常量,判断const引用是否合法就是去判断能否通过其它方式修改const值,只要无法修改,就是合法的。非const引用只能绑定到与该引用同类型的对象。const引用则可以绑定到不同但相关的类型的对象或绑定到右值(就是可以用常数赋值)。比如const int型引用可以绑定到double型。
int i=0;
int &j=i;
int i = 42;
const int &r = 42;
const int &r2 = r + i;
13.C#中可以通过using定义别名;C++中是使用typedef去定义别名,这么做通常了为了强调类型使用的目的,或者简化负责类型的定义。
using Tree=MyNamespace.Tree;
//C++
typedef in Age;
14.C#中枚举可以定义成int外的值类型;C++只使用int型。使用相应整数值给枚举型赋值在两种语言中都是不允许的。
15.C#中struct与class代表了值类型与引用类型,它们的差别主要是由分配的位置引起的,比如值类型主要分配在线程堆栈中,引用类型分配在托管堆中。这个存储位置的不同导致了很多特性的不同,比如值类型传递默认是值拷贝,引用类型默认是引用拷贝,值类型是封闭的,引用类型可以继承等等。在C++中,struct与class都是定义类的关键字,它们的唯一区别在于struct定义的类默认的访问级别是public的,class定义的类默认的访问级别是private的。
16.C#的基本类型(指针等类型不考虑)分为值类型(除了string外的基本类型基本上都是值类型,表现为定义成了struct)与引用类型(string类型,类,接口,数组,集合等)两种,类型系统的根类型是Object。由于Object是类,所以是引用类型。这样就导致了很多时候,某些操作需要Object类型,但是传进来的是值类型,为了正常的进行操作,就需要把值类型添加上必要的一些指针,包装成引用类型,这个过程就是大家熟知的装箱类型。当需要值类型的值的时候,就需要把包装好的类型中的值部分再Copy出来使用,这个就是拆箱的过程。这是一个C#中严重影响效率的操作,基本上从某些方面上说,这个直接导致了泛型的诞生。
17.C#中类成员的访问级别有public,private,protected,internal以及protected internal几种。C++中成员的级别通常也是3种:public,private与protected,而且可以很多成员公用一个访问级别关键字。我觉得C++中友元是破坏封装性的,所以我自己很少会用,一般这种问题都是可以提供一个public方法让别的类去访问的。
18.C#中定义类的时候,类体外的"{}"后面不需要";"。而C++中定义类的时候,需要在类后面的"{}"后面加上";"。这个错误比较隐蔽,特别是对习惯于C#的人。
19.C#比C++少了头文件的概念。C++中的头文件一般只用于放声明(extern),而不放定义。但是对于编译时可以确定的一些数据,也可以放在头文件中,比如使用常量定义的const值(不是用常数定义的const不放在头文件中),或者是inline函数。
20.C++程序需要包含所使用对象的头文件(#include),但是需要避免多次包含相同的头文件,所以一般需要结合#define,#ifndef来防止多次包含相同的头文件。C#不存在重复引用的问题。
#define XXX
// ...
#endif
特别推荐:
1.C#与C++都推荐在变量使用的地方定义,方便阅读与赋值。
2.C#与C++都推荐从行为开始分析与设计类。