1、标识符命名规则!
标识符不能包含两个连续的下划线,也不能以下划线开头后面紧跟一个大写字母。有些标识符(在函数外定义的标识符)不能以下划线开头。
但是在G++编译器和VC编译器下,二者均可正确编译!
2、跨平台编译程序!
这里不是要讲解如何跨平台编译程序,也不是告诉你如何更好地编写通用平台的程序规则,那可能涉及到很多的宏定义以及硬件相关特性。这里仅为使用示例代码提供一种精简的方式。
用Eclipse+MinGW的方式默认会很精简,所以把它当作一种目标!
用Visual Studio 2008创建的程序会让你引入预编译头stdafx.h(这通常发生在使用Visual Studio创建Win32控制台应用程序,并直接点击“完成”后),这将导致你无法将在Eclipse上编写的程序直接运行在Visual Studio上。这时你应该通过修改项目属性来获得这种精简的方式:(选择项目,右键属性,选择配置属性->C/C++->预编译头->创建/使用预编译头,选择“不使用预编译头”->“确定”后再次编译即可!)
3、变量命名习题
//测试变量命名!
//error C2632: “int”后面的“double”非法
//int double = 3.14159;
//-------------------------------------------------
char _='a';
std::cout<<_<<std::endl;
//-------------------------------------------------
//warning C4091: “”: 没有声明变量时忽略“bool”的左侧
//error C2059: 语法错误: “-”
//bool catch-22;
//-------------------------------------------------
//error C2059: 语法错误: “数字上的错误后缀”
//char 1_or_2 = '1';
//-------------------------------------------------
float Float=3.14f;
std::cout<<Float<<std::endl;
4、在C++中,“初始化不是赋值”
初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。
int ival(1024); //直接初始化
int ival = 1024; //复制初始化
直接初始化语法更灵活,效率更高!
对内置类型来说,复制初始化和直接初始化几乎没有差别。
对类类型来说,有些初始化仅能用直接初始化完成。要想理解其中缘由,需要初步了解类是如何控制初始化的。
例如:
也可以通过一个计数器和一个字符初始化string对象。这样创建的对象包含重复多次的指定字符,重复次数由计数器指定:
std::string all_nines(10, ‘9’); //all_nines = “9999999999”;
本例中,初始化all_nines的唯一方法是直接初始化。有多个初始化式时不能使用复制初始化。(V注:这里的初始化式即为构造函数的多个重载;这里所谓的“不能使用”应该是“功能有所不及”的意思!)
5、变量初始化规则
使用未初始化的变量经常导致错误,而且十分隐蔽。问题出在未初始化的变量事实上都有一个值。编译器把该变量放到内存中的某个位置,而把这个位置的无论哪种位模式都当成是变量初始的状态。当被解释成整型值时,任何位模式都是合法的值——虽然这个值不可能是程序员想要的。因为这个值合法,所以使用它也不可能导致程序崩溃。可能的结果是导致程序错误执行和/或错误计算。
//在Eclipse中运行没有出现错误!
//在Visual Studio中运行出现运行时错误!
int ival; //没有初始化!
std::cout<<ival<<std::endl;
6、声明和定义
为了能让多个文件访问相同的变量,C++区分了声明和定义。简单地说就是可以用extern关键字来声明,任何有分配内存行为的声明都是定义。定义也是声明。声明:标明变量的类型和名字;定义:为变量分配存储空间,还可以为变量指定初始值。
举例说明:
extern double pi; //声明
double pi; //定义,声明了pi同时定义了pi
extern double pi = 3.14159; //定义,因为它为pi分配了初值。只有当该extern语句
位于函数外部的时候才允许使用初始化式,否则将导致编译错误。
7、变量的隐藏:
std::string s1 = "I am a std::string!";
std::cout<<s1<<std::endl;
for(int s1=3; s1!=0; --s1)
std::cout<<"I am a number(int):"<<s1<<std::endl;
提示:在Visual Studio 2008中使用std::string定义一个变量,再通过std::cout将其输出,将会得到“error C2679: 二进制“<<”: 没有找到接受“std::string”类型的右操作数的运算符(或没有可接受的转换)”错误信息,这时要检查头文件中是否包含#include <string>。而在Eclipse中则不用如此设置(具体看编译器版本)。这与标准库实现的具体细节有关,在MSVC中,它在文件Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\string中被实现,在GNU中,它在base_string.h中被实现。在使用std::string时,总是包含#include <string>是一个好习惯!
8、const对象默认为文件的局部变量
一般声明变量后可以在其它文件中通过extern关键字声明并使用该变量:
//文件1:
int counter;
//文件2:
extern int counter;
++counter;
但是如果是const则无法访问。可以通过显式指定extern关键字使其成为全局可访问对象:
//文件1:
extern const int bufSize = getBufSize();
//文件2:
extern count int bufSize;
//……使用bufSize
注解:非const变量默认为extern。要使const变量能够在其他的文件中访问,必须显式地指定它为extern。
9、引用
int ival = 1024;
int &refVal = ival;
当引用初始化后,只要该引用存在,就保持绑定到初始化时指向的对象。不可能将引用绑定到另一个对象。
也正因为如此,所以引用比指针的优势就在于:引用不可以在方法中篡改,这使得方法变量变得安全了。
10、const引用
const int ival = 1024;
const int &refVal = ival;
这里我们要求左侧的类型是一致的,包括const!
非const引用只能绑定到与该引用同类型的对象。
const引用则可以绑定到不同但相关的类型的对象或绑定到右值。(具体示例详见C++Primer v4 P52)
例如:
//错误代码
double dval = 3.14;
const int &ri = dval;
编译器会将这些代码转换成如以下形式的编码:
int temp = dval;
const int &ri = temp;
如果ri不是const,那么可以给ri赋一新值。这样做不会修改dval,而是修改了temp。期望对ri的赋值会修改dval的程序员会发现dval并没有被修改。仅允许const引用绑定到需要临时使用的值完全避免了这个问题,因为const引用是只读的。
但是如下代码可以执行:
int ival = 1024;
const int &refVal = ival;
++ival;
//++refVal; //error C3892: “refVal” 不能给常量赋值
std::cout<<"ival="<<ival<<"\trefVal="<<refVal<<std::endl;
输出:ival=1025 refVal=1025
const double dval = 3.14;
const int &ri = (int)dval;
std::cout<<ri<<std::endl;
输出:3