C++Primer 第二章

//1.程序尽量避免依赖于实现环境的行为。比如:如果将int的尺寸看成一个确定不变的已知值,那么这样的程序就称为不可移植的。
typedef int int32; //使用类似的typedef,可以有效的解决移植问题 //2.进行算术运算的时候,要注意有符号类型向无符号类型的提升
long long value = (int)- + (unsigned int); //value = 4294967295 //3.一个行如42的值被称为字面值常量,这样的值一看便知。每个字面值常量对应一种数据类型,字面值常量的形式和值决定了其数据类型。 //4.泛化的转义序列:形式是\x(注意是小写的x)后紧跟十六进制数字,或者\后紧跟1个,2个或者3个八进制数字,其中数字部分表示字符对应的数值
char *str0 = "\x31";     //str0 = "1"
char *str1 = "\X31";     //str0 = "X31"
char *str2 = "\0615";    //str2 = "15" 注意这里仅有前3个数字与\构成转义序列 //5.可以通过为字面值常量添加前缀或者后缀来改变其默认类型,字符串都是添加前缀,算数类型都是添加后缀
int value0 = sizeof L'a'; //value0 = 2 L前缀 表示宽字符 对应 wchar_t 类型 int value1 = sizeof ~0U; //value1 = 4 u或U后缀 对于整形 表示最小匹配类型为 unsigned
int value2 = sizeof ~0L; //value2 = 4 L或l(字母l) 对于整形 表示最小匹配类型为 long
int value3 = sizeof ~0LL; //value2 = 8 LL或ll(字母l) 对于整形 表示最小匹配类型为 long long int value4 = sizeof 0.1; //value4 = 8 默认以double存储浮点数
int value5 = sizeof 0.1F; //value5 = 4 F或f后缀 对于浮点型 表示float
int value6 = sizeof .1L; //value6 = 8 L或l后缀 对于浮点型 表示long double unsigned long long value7 = ~0LL; //value7 = 0xffffffffffffffff 注意LL表示最小匹配类型为long long 而不是指定为long long, U L等后缀类似。
unsigned long long value8 = 0xFFFFFFFFFFFFFFFFL; //value8 = 0xffffffffffffffff 再次强调是最小匹配类型。 //6.定义于任何函数体之外的变量被初始化为0,定义在函数体内的变量不被初始化,任何试图访问和拷贝没有初始化的变量将会出错 //7.C++将声明和定义区分开来,声明使得名字为程序所知。定义负责创建与名字关联的实体。变量仅能定义一次,但能声明多次。
extern int value0; //声明
int value1; //定义
extern int value2 = ; //定义 //8.变量命名规范:变量名一般为小写开头,用户自定义类型一般为大写开头。对于多个单词组成的变量,应用驼峰命名法。 //9.作用域操作符::有3种用法
::name; //全局作用域
class::name; //类作用域
namespace::name //命名空间作用域 //10.一条声明语句由一个基本数据类型和紧随其后的一个声明符列表组成。每个声明符命名了一个变量并指定了该变量为与基本数据类型有关的某种类型。在同一条声明语句中,基本数据类型是相同的,但是声明符不一定相同。
// 基本类型:是类型说明符,可用const修饰,在声明语句中位于声明符之前。基本类型提供了最常见的数据类型,以此基础构建声明符。
// 声明符:是声明的一部分,包括被定义对象的名字和类型修饰符。其中类型修饰符(比如const,registe等)可有可无。
int value0 = ; //此定义语句中, int为基本数据类型, value0为声明符。此声明语句的结果是将声明符value指定为int类型。
int *pvalue0, value1;   //在此定义语句中,定义了2个类型不同的变量。pvalue0为int*类型,其类型修饰符*是声明符的一部分, value1为int类型,它们的基本数据类型都是int。
//11.引用:为对象起了另一个名字,引用必须初始化,引用无法重新绑定到另一个对象上。引用本身不是一个对象。不能直接定义引用的引用(可以通过类型别名来间接定义),不能在容器中存储引用类型,不能定义一种元素类型为引用类型的数组。
// 引用类型要和其绑定的对象严格匹配,但是有两个例外:1.在初始化常量引用的时候,允许使用任意能转化为该引用类型的表达式作为初始值。2.父类引用能接受子类对象。
double data0 = 3.14;
const int &data1 = data0; //合法,但data1和data0的地址不同 //12.指针:是指向另一种类型的复合类型。其本身就是一个对象。 void*指针是一种特殊的指针,可以用于存放任意对象的地址,不能对void*指针进行解引用。
// 指针类型必须和所指对象严格匹配,但是有两个例外:
// 1.允许一个指向常量的指针指向一个非常量对象。但这样的赋值有一个前提:只进行一层间接运算:int* 可以转化为const int *,而无法从int **”转换为“const int **
// 2.父类指针能指向子类对象。
int number = ;
const int *pNumber0 = &number;
int *const pNumber1 = &number; //离pNumber1最近的符号是const,意味着pNumber1本身是一个常量,其类型由声明符的其余部分决定。 //13.默认情况下const对象被设定为文件内有效。当多个文件中出现了同名const变量,等同于在不同文件中分别定义了这些独立的变量。若在头文件定义了一个const变量,那么多个包含此头文件的源文件中都将定义自己的独立的const变量。
// 如希望将const变量进行文件间共享,则在一个源文件中进行定义,在头文件中进行extern声明即可,那么所有包含此头文件的源文件都将共享一个const变量。
// const分为顶层const和底层const。顶层const可以表示任意的对象是常量,而底层const表示指针所指的对象是常量。由于引用不是对象,所以引用类型的const都是底层const。
// 在执行对象拷贝的时候,常量是顶层const还是底层const有明显区别。其中,顶层const不受影响。另一方面,底层const对象执行拷贝操作的时候,要求拷入和拷出的对象必须具有相同的底层const资格或者两个对象的数据类型能够转换。一般来说非常量可以转换为常量,反之则不行。
// const是一种类型修饰符
const int *p0 = nullptr; //此处的const是底层const
int *const p1 = nullptr; //此处的const是顶层const
const int count = ; //此处的const是顶层const //14.类型别名:使用关键字typedef。
typedef int* pint;
int nData = ;
const pint pData = &nData; //等价于: int *const pData = &nData; 注意点:声明中用到了pint,其基本数据类型是指针,若写成 const int *pData = &nData;则其基本数据类型是int, *变成了声明符的一部分。
//pData = nullptr; //错误 error C3892: “pData”: 不能给常量赋值

    typedef class CTest0
    {
      public:
      int i;
    }Test0; //Test0是类型


    class CTest1
    {
      public:
      int i;
    }Test1; //Test1是变量


   Test0 T0;
   //Test0.i = 10; //错误
   //Test1 T1; //错误
   Test1.i = 10;

//15.auto:让编译器通过初始值来推断变量的类型。显然,auto定义的变量必须具有初始值,且其基本类型必须相同。
// auto在下列几种情况下推断出的类型与其初始值并不完全一样,编译器适当的改变结果类型使其更加符合初始化规则
// A:编译器以引用对象的类型作为auto的类型。
// B:auto会忽略顶层const并保留底层const
// C:auto会将函数名转为对应函数指针类型
// D:auto会将数组名转为对应指针类型 //16.decltype:希望从表达式中推断出要定义的变量的类型,但是不想用该表达式的值来初始化变量。在此过程中,只得到表达式的类型而不实际计算表达式的值。
// decltype不会执行auto中编译器执行的转换。
// 如果decltype使用的表达式不是一个变量,则decltype返回表达式对应的类型。如果表达式的求值结果是左值,则decltype作用于该表达式(不是一个变量)得到一个引用类型。
// 左值:当对象用作左值的时候,使用的是对象的身份(在内存中的位置)
// 右值:当对象用作右值的时候,使用的是对象的值(内容)
// 赋值运算符:需要一个非常量左值作为其左侧运算符对象,得到的结果也是一个左值
// 取地址符:作用于一个左值运算对象,返回一个指向该运算对象的指针,这个指针是一个右值
// 解引用运算符和下标运算符:求值结果均为左值
// 前置递增递减运算符:求值结果为左值
// 后置递增递减运算符:求值结果为右值
int nNumber0 = ;
int *pnNumber0 = &nNumber0;
decltype (nNumber0) nNumber1; //nNumber1为int类型
decltype (pnNumber0) nNumber2; //nNumber2为int*类型
decltype ((nNumber0)) nNumber3 = nNumber0; //nNumber3为int&类型
decltype (*pnNumber0) nNumber4 = nNumber0; //nNumber4为int&类型 //17.结构体和类的类体的后面都可以跟其对象的定义
class A
{
} a; //a为类A的一个对象 //18.当预处理器看到#include标记时,就会用指定的头文件的内容代替#include
// 通常使用#ifdef #endif 来防止重复包含。
// 预处理变量无视C++语言中关于作用域的限制 #define 指示的接受后面的名字,并把这个名字定义为预处理器变量,常用大写。
// 在程序编译之前,预处理器负责将程序中的预处理变量替换为它的真实值 //19.在数值上数组名等同于对数组名取地址,但是意义上不同,前者是一个数组首地址,后者为一个指向数组的指针
int array[] = {};
int size0 = sizeof array; //size0 = 40
int size1 = sizeof (&array); //size1 = 4 //20.常量表达式:是指值不会变,并且在编译过程就能得到计算结果的表达式
int fun(){return ;}
const int nValue0 = ; //常量表达式
const int nValue1 = nValue0 + ; //常量表达式
int nValue2 = ; //并不是常量表达式,原因是:value2的值可以改变
const int nValue3 = fun(); //并不是常量表达式,原因是:value3的值不能在编译的时候得到
上一篇:SVN trunk、branch、tag的用法


下一篇:Codevs 1009 产生数