声明,所有的朋友,如果要转我的帖子,务必注明"作者:黑啤 来源:CSDN博客"和具体的网络地址http://blog.csdn.net/nx500/archive/2007/10/21/1836075.aspx,并且我的所有博客内容,禁止任何形式的商业用途,请大家尊重我的劳动.谢谢!
目 录
二.变量和基本类型.
001 bool/char/wchar_t/short/int/long/float/double/long double.
002 signed/unsigned.
003 如果把-1赋给8位的unsigned char,那么结果就是255,因为255就是-1对256求模后的值(补码).
//
以下这段原码/反码/补码的说明摘录自muxiaodan的博客
原码:原码就是这个数本身的二进制形式.
例如
0000001 就是 + 1
1000001 就是 - 1
反码:正数的反码与原码相同.负数的反码是将其原码除符号位之外的各位求反.
[ - 3 ]反 = [ 10000011 ]反 = 11111100
补码:正数的补码与原码相同.负数的补码是将其原码除符号位之外的各位求反之后在末位再加1.
[ - 3 ]补 = [ 10000011 ]补 = 11111101
特别注意,如果 + 1之后有进位的,要一直往前进位,包括符号位 ! 1000 0000 = - 128
为什么要设立补码呢 ?
第一是为了能让计算机执行减法.
[a - b]补 = a补 + ( - b)补
第二个原因是为了统一正0和负0.
正零: 00000000
负零: 10000000
这两个数其实都是0,但他们的原码却有不同的表示.但是他们的补码是一样的,都是00000000.
// http://hi.baidu.com/muxiaoduan/blog/item/0b8e7d2e1239c8534fc22600.html
原码:原码就是这个数本身的二进制形式.
例如
0000001 就是 + 1
1000001 就是 - 1
反码:正数的反码与原码相同.负数的反码是将其原码除符号位之外的各位求反.
[ - 3 ]反 = [ 10000011 ]反 = 11111100
补码:正数的补码与原码相同.负数的补码是将其原码除符号位之外的各位求反之后在末位再加1.
[ - 3 ]补 = [ 10000011 ]补 = 11111101
特别注意,如果 + 1之后有进位的,要一直往前进位,包括符号位 ! 1000 0000 = - 128
为什么要设立补码呢 ?
第一是为了能让计算机执行减法.
[a - b]补 = a补 + ( - b)补
第二个原因是为了统一正0和负0.
正零: 00000000
负零: 10000000
这两个数其实都是0,但他们的原码却有不同的表示.但是他们的补码是一样的,都是00000000.
// http://hi.baidu.com/muxiaoduan/blog/item/0b8e7d2e1239c8534fc22600.html
005 unsigned/long 128u/22L/33uL.
006 浮点数/单精度/扩展精度 3.14f/3.141593f/3.1415926L.
007 '/0' NULL character/空字符(定义在cstdlib中).
008 L "a wide string literal" 宽字符字面值是一串常量宽字符,同样以一个"宽"空字符结束.
009 lvalue/rvalue (发音:ell-value/are-value) 能出现在赋值语句左边的/只能出现在赋值语句右边的.
010 对象:就是内存中具有类型的区域.
011 标识符:由数字/字母/下划线组成,不能以数字开头,且大小写敏感.
不能包含两个连续的下划线,也不能以下划线开头紧跟一个大写字母.函数外定义的标识符"不能"以下划线开头.
012 类型决定了分配给变量的存储空间的大小和可以在其上执行的操作.
013 复制初始化/copy-initialization: int ival = 1024;
014 直接初始化/direc-initialization: int ival(1024); 直接初始化语法更灵活,效率更高.
015 初始化不是赋值,初始化是创建变量是赋给的初始值,赋值是擦除当前值并赋给新的值
016 内置类型变量是否自动初始化取决于变量定义的位置.在函数体外定义的变量都初始化成0,在函数体里定义的内置类型不进行自动初始化.
017 如果定义某个类的对象时,没有提供初始化式,这个类也可以定义初始化时的操作.它是通过定义一个默认的构造函数来实现的.
std::string emp; //emp is an empty string;
018 变量的定义:用于为变量分配存储空间,可以指定初始值,同一个变量在一个程序中有且仅有一个定义.
019 变量的声明:用于向程序标明变量的类型和名字,通过extern关键字声明变量而不定义它.如果声明有初始化,那么它可以被当做是定义.
只有当extern声明位于函数外部,可以初始化.
extern double pi = 3.14159;
020 用来区分名字的不同意义的上下文称为作用域.全局作用域/局部作用域/语句作用域/类作用域/命名空间作用域.
021 const限定符.
常量在定义后不能被修改,所以" 必须"在定义时赋值.
const std::string hi = "hello heipi";
const int i = 0;
在全局作用域声明的const对象是定义该对象文件的局部变量.可以通过指定该变量为extern使之成为全局变量.
extern const int buffSize = 1024;
022 引用:就是对象的另一个名字.在实际程序中,引用主要用做函数的形式参数.
不能定义引用类型的引用.
引用必须用与该引用同类型的对象初始化.
int ival = 1024;
int &refval = ival; // ok
int &refva2; // error, 没有初始化
int &refva3 = 0; // error, 必须用一个对象初始化
不能将一个引用绑定到另一个对象上.初始化是指明引用指向那个对象的唯一方法.
const引用是指向const对象的引用.
const int ival = 1024;
const int &refval = ival;// ok
int &ref2 = ival; // error 不能将非常量引用指向一个常量类型
非const引用只能绑定到与该引用同类型的对象." 但"const引用可以绑定到不同但相关的类型的对象或绑定到右值.
int i = 0;
const int &r1 = 42; // ok
const int &r2 = r1 + i; // ok
double dval = 3.14;
const int &rd = dval; // rd == 3
023 typedef可以用来定义类型的同义词.
1.隐藏特定类型的实现,强调类型的目的.
2.简化复杂类型定义,容易理解.
3.允许一个类型用于多个目的.
typedef double wages;
typedef double salary;
024 枚举enum,第一个枚举成员赋值为0,可以通过中间赋值而修改枚举元素的值.
enum color {red, blue, white=1, black}; // red:0 blue:1 white:1 blacl:2.
color cl = blue;
025 定义类:class开始,分号结束.
class Sales_item {
public:
// ...
private:
std::string isbn;
unsigned units_sold;
double revenue;
}; // 注意结尾的分号
类的数据成员是在构造函数里初始化的.
使用class定义类,第一个访问标号前的任何成员隐式的指定为private,而用struct定义类默认为public.
026 头文件一般包含:1.类的定义;2.exteren变量的声明;3.函数的声明.
好处:1.保证所有的文件使用给定实体的同一声明;2.当声明需要修改时,只有头文件需要修改.
因为头文件被多个源文件包含,所以不应该包含变量或函数定义.例外:头文件可以定义类/值在编译时就已经知道的const对像和inline函数.
027 避免头文件的多重包含
#ifndef SALESITEM_H
#define SALESITEM_H
...
#endif
// 以下的总结来自于CSDN社区"C/C++/新手乐园"的不接不舒服斯基帖子
// http://topic.csdn.net/u/20070922/23/f2aaf4a5-7960-4cda-84a3-fa1de28f003d.html
C++中const总结
一:对于基本声明
1.const int r=100;
//标准const变量声明加初始化,因为默认内部连接所以必须被初始化,其作用域为此文件,编译器经过类型检查后直接用100在编译时替换.
2.extend const int r=100;
//将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义.
3.const int r[]={1,2,3,4};
struct S {int a,b;};
const S s[]={(1,2),(3.4)};
//以上两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式
二:对于指针
1.const int *r=&x;
//声明r为一个指向常量的x的指针,r指向的对象不能被修改,但他可以指向任何地址的常量.
2.int const *r=&x;//与用法1完全等价,没有任何区别。
3.int * const r=&x;
//声明r为一个常量指针,他指向x,r这个指针的指向不能被修改,但他指向的地址的内容可以修改.
4.const int * const r=&x;
//综合1,3用法,r是一个指向常量的常量型指针.
三:对于类型检查
可以把一个非const对象赋给一个指向const的指针,因为有时候我们不想从这个指针来修改其对象的值,但是不可以把一个const对象赋值给一个非 const指针,因为这样可能会通过这个指针改变指向对象的值,但也存在使这种操作通过的合法化写法,使用类型强制转换可以通过指针改变const对象:
const int r=100;
int *ptr=const_cast <int* >(&r);//C++标准,C语言使用:int* ptr =(int*)&r;
四:对于字符数组
如char * name = "china";
这样的语句,在编译时是能够通过的,但是"china"是常量字符数组,任何想修改他的操作也能通过编译但会引起运行时错误,如果我们想修改字符数组的话就要使用char name[]="china";这种形式.
五:对于函数
1.void Fuction1(const int r);
//此处为参数传递const值,意义是变量初值不能被函数改变
2.const int Fuction1(int);
//此处返回const值,意思指返回的原函数里的变量的初值不能被修改,但是函数按值返回的这个变量被制成副本,能不能被修改就没有了意义,它可以被赋 给任何的const或非const类型变量,完全不需要加上这个const关键字.但这只对于内部类型而言(因为内部类型返回的肯定是一个值,而不会返回 一个变量,不会作为左值使用),对于用户自定义类型,返回值是常量是非常重要的,见下面条款3
3.Class CX; //内部有构造函数,声明如CX(int r =0)
CX Fuction1 () { return CX(); }
const CX Fuction2 () { return CX(); }
如有上面的自定义类CX,和函数Fuction1()和Fuction2(),我们进行如下操作时:
Fuction1()=CX(1); //没有问题,可以作为左值调用
Fuction2()=CX(1); //编译错误,const返回值禁止作为左值调用.因为左值
把返回值作为变量会修改其返回值,const声明禁止这种修改.
4.函数中指针的const传递和返回
int F1 (const char * pstr);
//作为传递的时候使用const修饰可以保证不会通过这个指针来修改传递参数的初值,这里在函数内部任何修改*pstr的企图都会引起编译错误.
const char* F2();
//意义是函数返回的指针指向的对象是一个const对象,它必须赋给一个同样是指向const对象的指针.
const char* const F3();
//比上面多了一个const,这个const的意义只是在他被用作左值时有效,它表明这个指针除了指向const对象外,它本身也不能被修改,所以就不能当作左值来处理.
5.函数中引用的const传递
void F1 (const X& px);
//这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的 副本,然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const 属性,且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.
六:对于类
1.首先,对于const的成员变量,只能在构造函数里使用初始化成员列表来初始化,试图在构造函数体内进行初始化const成员变量会引起编译错误.初 始化成员列表形如:X::X(int ir):r(ir){} //假设r是类X的const成员变量
2.const成员函数.提到这个概念首先要谈到const对象,正象内置类型能够定义const对象一样(const int r=10;),用户自定义类型也可以定义const对象(const X px(10);),编译器要保证这个对象在其生命周期内不能够被改变.如果你定义了这样的一个const对象,那么对于这个对象的一切非const成员函 数的调用,编译器为了保证对象的const特性,都会禁止并在编译期间报错.所以如果你想让你的成员函数能够在const对象上进行操作的话,就要把这个 函数声明为const成员函数.
假如f()是类中的成员函数的话,它的声明形如:
int f()const;
//const放在函数的最后,编译器会对这个函数进行检查,在这个函数中的任何试图改变成员变量和调用非const成员函数的操作都被视为非法
注意:类的构造和析构函数都不能是const函数.
3.建立了一个const成员函数,但仍然想用这个函数改变对象内部的数据.这样的一个要求也会经常遇到,尤其是在一个苛刻的面试考官那里.首先我们要弄 清楚考官的要求,因为有两种方法可以实现,如果要求不改变原来类的任何东西,只让你从当前这个const成员函数入手,那么你只有使用前面提到的类型强制 转换方法.实例如下:
//假如有一个叫做X的类,它有一个int成员变量r,我们需要通过一个const成员函
数f()来对这个r进行++r操作,代码如下:
void X::f()const
{const_cast <X* >(this)- >++r; } //通过this指针进行类型强制转换实现
另外一种方法就是使用关键字:mutable.
如果你的成员变量在定义时是这个样子的:mutable int r;
那么它就告诉编译器这个成员变量可以通过const成员函数改变.编译器就不会再理会对他的检查了