《C++Primer》学习笔记(1-5章)



第一章 文件头及声明

关于extern

使用extern 声明而不定义,它是说明变量定义在程序其他地方

全局不初始化的extern int i; 是声明不定义;只要声明并且有初始化式,那么就是定义;带有extern且有初始化的声明(也是定义),比如extern float fval =2.34; 这种必须放在函数外面,否则出错

文件B要访问另外一个文件A中定义的变量,那么在B中必须先extern声明一下,并且不需要include A。另外,A中定义的变量一定是全局变量。 

Extern C

 const常量在函数外定义默认是文件级,别人不可访问。要想成为程序级(被其他文件访问)必须要加extern。A.pp中extern const int ival=23;而在B.cpp中extern const int ival;声明一下就可以使用(声明时const不是必须,但最好加上)。另外,const常量在声明的时候必须初始化,如果是使用常量表达式初始化,最好放在头文件去定义(头文件特殊的可以放定义的三个之一)。否则只能放在源文件中定义,并加上extern以能被多个文件共享。

struct MSGMAP_ENTRY {

    UINT nMessage;

    void (*pfn)(HWND, UINT, WPARAM,LPARAM);

};

struct MSGMAP_ENTRY _messageEntres[] = {

    WM_LBUTTONDOWN, OnLButtonDown,

    WM_RBUTTONDOWN, OnRButtonDown,

    WM_PAINT, OnPaint,

    WM_DESTROY, OnDestroy

};

关于结构体的说明:

上面定义了一种结构体类型后,后面要定义数据类型时就要struct MSGMAP_ENTRY  变量名。 蓝色部分就是当变量类型使用

第二章 变量和基本类型

1.      只有内置类型存在字面值,没有类类型或标准库类型的字面值(可以这样理解,是内置类型组成了其他类型)。

C++中有整型字面值,浮点字面值,布尔字面值和字符字面值,字符串字面值,转义序列,多行字面值

2.       下面

a)        ‘数字1’===0x31 (49)

b)        ‘A’===0x41 (65)

c)        ‘a’=====0x61(97)

3.        由空格,制表,换行连接的字符串字面值可以连接成一个新的字符串

但是连接字符串与宽字符串的结果就不可预料了

代码续行 \后面不能有空格,只能是回车,接下来的一行要从头开始,没有缩进   字符串换行要加/   ===》小线倾斜的方向不一样

4.       1024f有错,整数后面不能有f

5.       2.34UL有错,浮点数后面不能有U

6.       标识符不能以数字开头

7.       直接初始化与复制初始化 int ival = 123;  int ival(34);

8.       int ival = 09; 错! 八进制数,不能有大于等于8的数字

9.       函数外定义的变量初始化为0,函数内的变量不进行初始化(可能是一些无意义的值但是合法的值,所以造成错,所有编译器难以发现所有这类错)

所以,建议每个内置类型对象都要初始化

在函数外定义的类类型对象使用默认构造函数初始化,对于没有默认构造函数的类型,要显式初始化,即使用带参构造函数

10.   初始化不是赋值。初始化要分配空间并给初值,而赋值则是要替换当前值

11.   声明与定义的区别

a)        定义要分配空间,一个变量程序中只能定义一次

b)       声明用于向程序表明自己的名字和类型,程序中可以出现多次定义也是声明,定义的时候也声明了它的名字和类型

c)        使用extern 声明而不定义,它是说明变量定义在程序其他地方

函数内或外int i; 都是定义(都分配空间,但函数内没有初始化为0)  // 不能放在头文件中。

d)       全局不初始化的extern int i; 是声明不定义;只要声明并且有初始化式,那么就是定义;带有extern且有初始化的声明(也是定义),比如extern float fval = 2.34;这种必须放在函数外面,否则出错

e)        文件B要访问另外一个文件A中定义的变量,那么在B中必须先extern声明一下,并且不需要include A。另外,A中定义的变量一定是全局变量。

f)        在正式编写程序语句前定义的一些全局变量或局部变量,在C中为声明,C++中为定义 ( int  a;//在标C中为声明,是不可执行语句;在C++中为定义)

12.   定义在函数外的变量有全局作用域

13.  局部同名变量屏蔽了上层作用域的变量,烂程序,不易读。在局部中要想使用全局中的同名变量在变量前使用域作用符::

14.  在内建数据类型的情况下,++i与i++效率没区别,在自定义数据类型的情况下++i的效率高

15.   c++中,定义和声明可以放在任何可以放语句的位置,所以,通常把一个对象定义在首次使用它的地方是一个很好的办法

16.   const int pi = 3.14; 采用const易维护(在多次出现3.14的地方使用const变量来代替)

再比如:

const int max = 23;

for (int i = 0; i != max; ++i)

标准C++中,for中定义的i只在语句作用域中(for语句),出了for就不可见了

17.   全局变量的程序级与文件级,   const全局变量

a)        普通变量在函数外定义就是程序级,别的文件要使用只先extern声明一下就行。并且不用include变量定义的文件

b)      const常量在函数外定义默认是文件级,别人不可访问。要想成为程序级(被其他文件访问)必须要加externA.ppextern const int ival=23;而在B.cpp中extern const int ival;声明一下就可以使用(声明时const不是必须,但最好加上)

c)       const常量在声明的时候必须初始化,如果是使用常量表达式初始化,最好放在头文件去定义(头文件特殊的可以放定义的三个之一)。否则只能放在源文件中定义,并加上extern以能被多个文件共享。

18.    关于const引用与非const引用与它们所绑定的对象的关系:(const引用是指向const对象的引用,非const引用是指向非const对象的引用)

a)        const引用类型必须要与它所引用的变量的类型一致(非const引用只能绑定到与该引用同类型的对象,而不能是右值)

int ival =12;

int &ref = dval 或34; //error  初始化的时候只能绑定到同类型的对象(不能是右值)。 非初始化的时候可以被赋值,引用与其绑定的对象的值都会改变

b)       const引用可以绑定到不同但相关(即可以转化)的类型的对象(可以是非const对象)或右值

比如[const] double dval= 3.14; //这里要不要const都行

const int &refVal = dval; // warning,编译器会中间把3.14转成int temp的3,然后再给了refVal,这里编译不出错,但是会给出警告

constint &refVal2 = 2.33; // warning,这里const不可少,不然不使用右值

refVal2 = dval; // error, const引用的值不可改变

19.   enum color {red, blue=4, white};其中white是5

枚举成员是常量而不能改变,所以初始化时要用常量表达式,const常量与整形字面值都是常量表达式

扩展color black = red; // 必须使用里面定义的来作为右值进行初始化

color pink = 3; // error

20.   设计类:从操作开始设计类。先定义接口,可以决定需要哪些数据以及是否需要私有函数来支撑公有函数

21.   class与struct定义类仅仅影响的是默认访问级别,struct为public,class为private

22.   c++支持分别编译separatecompilation,头文件和源文件。将main放在其他的源文件中.

头文件中有类定义,extern变量声明,变量和函数声明,带来的莫大的好处,一是统一性:保证所有文件使用的同一声明,二是易维护:需要修改时,只改头文件就行了,易维护

设计头文件时注意:声明最好放在一起。编译头文件需要一定的时间。有些C++编译器支持预编译头文件,需要查手册。

头文件用于声明,而不是定义——有三个例外类定义,用常量表达式初始化的const变量和inline函数的定义。它们可以在多个源文件中定义(相当于头文件被include到了多个源文件中),只要在多个源文件中的定义时相同的。

解释:允许在头文件中定义类和inline函数是因为编译器需要它们的定义来产生代码,对于类类型需要知道类对象的数据成员和操作才能分配空间。但是对于单个源文件A.cpp为了避免多重包含,在定义时须加#ifndef与#endif(头文件应该有保护符,会被其他文件包含)

如果将inline函数的定义放在某源文件中,那么其他源文件将永远访问不到

允许在头文件中定义const常量的原因:const常量是文件级作用域的,多个文件包含它互不影响,不算是重复定义。所以可以在头文件中定义。多个文件共享const变量的前提是必须保证多个文件使用的是相同的名称和值,将它放在头文件中,谁需要的时候就include它就行,安全方便。

另外要注意:在实际中,源文件里大多编译器只是像宏替换一样的使用常量表达式来替换const变量,而没有分配空间来存储用常量表达式初始化的const变量

如果const变量不是用常量表达式初始化的,这就不应该放在头文件中定义。此时它应该放在源文件中定义并初始化,加上extern以使它能够被多个文件共享。

23.  //Page58页小字部分:编译和链接多个源文件组成的程序

24.   关于inline函数

inline函数目的是:为了提高函数的执行效率(速度)。以目标代码的增加为代价来换取时间的节省。

非内联函数调用有栈内在的管理,包括栈创建和释放的开销。函数调用函数调用前保护好现场,返回后恢复现场,并按原来保存的地址继续执行。对于短小且频繁执行的函数,将影响程序的整体性能

C中可以用#define,编译器用复制宏代码的方式取代函数调用,但没有参数类型检查。

 

有两点特点注意的:
(1) 内联函数体中,不能有循环语句、if语句或switch语句,否则,函数定义时即使有inline关键字,编译器也会把该函数作为非内联函数处理。
(2) 内联函数要在函数被调用之前定义,否则内联失效。将它的定义放在头文件中就很好的可以做到这点,由于是在源文件中先include,再后面调用的,就保证了调用之前定义

(3)关键字inline必须与函数定义体放在一起才能使函数真正内联,仅把inline放在函数声明的前面不起任何作用。因为inline是一种用于定义的关键字,不是一种用于声明的关键字。(根据高质量C/C++指南,声明前不应该加,因为声明与定义不可混为一谈)

25.                                                                                                                                                                                                                                                     复合类型compoundtype,引用,数组,指针

第三章标准库类型

1.#include

usingstd::string;

   using std::cout;

2.头文件中定义确定需要的东西

3. 注意区分string类型与字符串字面值

4. Windows下用命令行要编译和运行.cpp文件

打开vs2005命令提示

cl /EHsc simple.cpp 生成exe文件//EHsc 命令行选项指示编译器启用 C++ 异常处理

若要运行 simple.exe 程序,请键入 simple 并按 Enter

要加入程序变量: simple < data\book_sales

vs2005中同样也可以加入程序变量,就在命令行参数里

5.         

a)  cin.getline(char*, int, char) // 是istream流

// 可以接收空格并输出,它的参数有三个,第三个是结束符,默认为'\n'

 

char ch[20];

       cin.getline(ch,5); 

       cout<< ch << endl; // 输入abcdefg,输出abcd最后一个为'\0'

                            //这个\0是自动加上的,所以可以cout << ch;

char ch[20];

       cin.getline(ch,5,'a');  // 遇到a结束,并加上\0

       cout<< ch << endl; //输入xyamnop,输出xy

 

b)       getline(cin, str), 须加#include // 是string流

        #include 
        istream& getline( istream& is, string& s, char delimiter = '\n' );  // delimiter分隔符
    接收一行字符串,可以接收空格并输出,遇到回车返回,并丢掉换行符。

不忽略行开头的换行符,如果第一字符是回车,那么str将是空string

逐行输出:

string str;

while(getline(cin, str)) {

       cout << line << endl; // 由于不含换行符,需要endl刷新输出缓冲区

}

逐词输出:

string str;

while (cin>> str) {

       cout << str << endl;

}     

c)        cin.get()吃掉没有用的字符,读到回车才退出

d)       char ch1[100],ch2[100];

cin >> ch1 >> ch2;  //接收字符串,忽略有效字符前的空白字符,以空白符(空格,TAB,回车)作为输入结束

getline(cin,str) 不忽略开头的空白字符,读取字符直到遇到换行符,读取终止并丢掉换行符

e)        gets(str) 接收一个字符串,可以接收空格并输出。加#include

与getline(cin,str)类似

f)        getchar()无参数,须加#include ,是C中的函数,尽量少用或不用

6.        str.size()的实现(返回有效字符的个数,不含最后的空字符,它的类型是string::size_type)

    constchar *st = "The expense of spirit\n";

    int len = 0;

    while (*st) { ++len; ++st; }

str.size的应用

for (string::size_type ix = 0; ix !=str1.size(); ++ix)

7.        string对象可以==, >=, <= !=操作

8.        string的赋值操作:str1 = str2;须先把str1的内存释放,然后再分配能存放副本大小的空间,再复制过来

9.        string s2 = “hello” + s1; //error,开头必须是变量

10. #include   p77页

isalnum(c)  字母数字

isalpha(c) 字母

iscntrl 控制字符

isdigit 数字

isgraph 不是空格但是可以打印

islower 小写

isprint 可打印

ispunct 标点

isspace 空格

isupper 大写

isxdigit 十六进制数

tolower 变小写

toupper

11. p78 使用C标准库头文件,使用#include 不要用#include ,这样保证标准库文件中的名字与std中的一致

12. string s;

cout << s[0] << endl; // error

13. t3_10,输入一个字符串,去掉里面的标点:

ispunct(ch)

result_str+= ch;

14. string是数据类型,vector是模板 vector是数据类型

15. vector v5(4); // 4个实例,必须有默认初始化构造函数

vectorv3(10,5); // 10个5

 

虽然能预先分配内存,但使用空vector来push_back更好

for(ix!=vector_size) vec.push_back(ix*5);

 

vectorivec; //是个空vector,没有分配空间,所以ivec[0]=3;//error

 

for(vector::size_type ix = 0; ix != ivec.size(); ++ix) //由于是动态变化的,所以这时使用函数

 

vectorivec(10); // 10个0

for(vector::const_iterator it = ivec.begin();it != ivec.end(); ++it) { // 如果ivec为空,则for不执行

        //cout << *it << endl;

       *it = 42;// error遍历只读

   }

vector的操作p81 , ==, !=, < >=

16. 迭代器可以==或!=操作,若指向同一个元素 则相等

迭代器的算法操作

 it+n size_type类型

 it-n  difference_type类型

 it1-it2 difference_type类型  it1或it2可以是vec.end()

记住:push_back()等改变长度的操作,使存在的迭代器全部失效!!

vector::iterator

17. bitset b; // n个0 //n必须是字面值或用常量值初始化的const对象

bitset b(u); // b是unsigned longu的一个副本

bitset b(s); // 字符串

bitset b(s, pos, n); // 字符串pos开始的n个

如:

bitset<16> bitvec1(0xffff); // 高十六个0去掉, 0到15位设为1

0xffff代表32位的unsigned long,高十六个0,低十六个1

bitset<128> bitvec2(0xffff); // 31位以上的都是0

string s(“1100”);  // s[0]值为1

bitset<8> bitvec3(s); // 读入位集的顺序是从右向左(用string最大下标元素来赋值给bitvec的最小下标值),结果是0000 1100,  这样bitvec[0]的值是最右的那个0

size_t sz = bitvec1.size(); // #include

//一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小

    if (bitvec.test(i))

    if (bitvec[i])

    bitvec.reset(); 全为0

    bitvec.set(); 全为1

    bitvec.flip(1);

    bitvec[1].flip();

    bitvec.flip(); // 全部取反

    uLong = bitvec.to_ulong(); 取回unsigned long值  

       // 若bitvec的位数128超过了unsigned long长度,那以会产生overflow_error异常

    t3_24:

    1,2,3,5,8,13,21的相应位置为1

    bitset<32> b;

    int x = 0, y = 1, z = x + y;

    while (z <= 21) {

       b.set(z);

       x = y;

       y = z;

       z = x + y;

}

第四章数组和指针

1.   int arr[32]; // 维数必须是字面值,枚举常量,常量表达式初始化的const变量

上面这句话在函数外,则初始化为0

定义在函数内,没有初始化,但分配了空间

    int arr[5] = {1,2,3}; // 剩下的初始化0,若是类类型就使用默认初始化构造函数,  java中不行,不能写维数的

    vector ivec = {1,2,3}; // errorvector没有这样的初始化

2.  数组不能直接复制和赋值

3.    数组下标越界导致:buffer overflow

4.   比较两个vector,先比较长度

5.   现代C++使用vector替换数组,使用string替换C风格字符串

6.   关于指针:

a)   string* s1, *s2看上去很不好,所以尽量将*与变量名放在一块

b)   避免使用未初始化的指针

C++没有办法检查出未初始化的指针,使用它可能会导致基础数据

如果要指向的对象还没有存在,那么就先不要定义这个指针。如果要定义,就=0或NULL#include

预处理器变量NULL不是在std命名空间中定义的,所以不是std::NULL

c)   void *

void* 这不叫空指针,这叫无确切类型指针.这个指针指向一块内存,却没有告诉程序该用何种方式来解释这片内存.所以这种类型的指针不能直接进行取内容的操作.必须先转成别的类型的指针才可以把内容解释出来;

'\0'不是空指针所指的内容,而是表示一个字符串的结尾,不是NULL

真正的空指针是说,这个指针没有指向一块有意义的内存,比如说:
char* p;
这里这个p就叫空指针.我们并未让它指向任意地点,又或者 char* p = NULL;这里这个p也叫空指针,因为它指向NULL 也就是0,注意是整数0,不是'\0'

一个空指针我们也无法对它进行取内容操作.
空指针只有在真正指向了一块有意义的内存后,我们才能对它取内容.也就是说要这样p = "hello world!"; 这时p就不是空指针了

后面会看到:不能使用void*指向const对象,必须使用const void *类型的指针保存const对象的地址

void *p; 只支持以下操作:

    与另一指针比较

    作参数或函数返回值

    给另一个void *赋值

7.   指针与引用的比较:

引用定义时必须初始化!

引用被赋值后改变的是所引用的对象

指针被赋值后将会指向另一个变量的地址,不专一

8.   int arr[5];

int *p = arr;

int *p2 = p + 2;// 那么,p的算术运算要求原   指针与计算出的新指针都要指向同一数组的元素,结果范围必须在[0,5],//注意这里5也算合法结果

    注意:C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作

    可以把指针看成是数组的迭代器

 

ptrdiff_tn = p1 – p2; // #include

    // 与机器相关:size_t是unsigned,   ptrdiff_t是signed

注意,ptrdiff_t只保证指向同一数组的两个指针间的距离,若是指向不同的指针,那么error

 

t4_17:

p1 += p2– p1; 若p1与p2指向同一数组那么始终合法,让p1指向p2所指向的地址

9.   关于const指针

a)   指向const对象的指针

const double pi = 3.14; // 不能用普通指针来指向它 但是相反地,可以用const double*的指针指向非const的变量

const double *cptr = 0或NULL; //这里const限定的是所指向的对象,而不是cptr本身,所以定义的时候可以不初始化, 蓝const是必须的,防止通过cptr来改变所指对象的值

cptr = pi; // 也可以在定义的时候初始化

指向const对象的指针可以指向非const对象

 

注意:不能保证cptr指向的对象的值一定不可修改!它完全有可能指向一个非const对象,将它赋给普通指针就可以改

double dval = 2.34; // 非const

cptr = &dval; //可以指向一个非const对象,但仍不可通过cptr来修改

const void *cpv = pi; // ok ,不能使用void*指向const对象,必须使用const void *类型的指针保存const对象的地址

t5_33: 将一个指向const对象的指针成void *

const string *ps;

void *pv;

pv = (void *)ps; // error!!!应该按下面写

pv = static_cast(const_cast<string*>(ps));

把ps变成string*之后再转成void*,就是说,先去掉const特性

static_cast跟(void *)差不多,就相当于强制转化

//============找回原来指针值,与上例t5_33没关系

对于void *pv;

小括号强制转化pc = (char*)pv; // 找回原来指针值

或者pc =static_cast(pv);

b)   const指针 //不能再指向别的对象,但能改变所指向对象的值  

int ival = 32;

int *const ptr = &ival; // 从右向左读:指向int对象的const指针

*ptr = 34; 则ival的值也变成34了。

但ptr = &ival2;  //error指针值不可变

c)   typedef与const指针

typedef string *pstring;

const pstring cstr; // 这里不要简单的理解为const string *cstr, 这里const限定的是指针,而不是所指对象!! string* const cstr 最好写成pstringconstcstr; 好理解

10.  t4_19:

const int ival; // error必须初始化

int *const ptr1; // error必须初始化

const int *ptr2; // ok,可以不用初始化

const int ival2 = 23; // 不能由指向非const对象的指针来指向它

int *const ptr2 = &ival2; // error,指向非const对象的指针不能指向const对象

11. p113讲:C风格字符串尽量少用,因为它常常带来很多错误,是导致大量安全问题的根源!

c-style character string

const char *cp = “hello world”;

while (*cp) {

    //TODO..

    ++cp;

}

c风格字符串的库函数:#include

strlen(s)// 返回有效长度

strcmp

strcat

strcpy

strncat(s1, s2, n) // 前n个字符, 返回s1

strncpy(s1, s2, n)

实参指针不能是空指针,即指向0NULL的指针。且指向以NULL结束的字符数组中的指针

必须确保目标字符串足够大,否则造成安全漏洞

char ca[] = {‘a’,’b’,’c’};

strlen(ca); // error!!!

使用strn比strcat strcpy更安全

strncat连接时,第二个字符串要覆盖掉第一个字符串的NULL

12. t4_25

两个string类型的比较可以使用>,< 而两个C风格字符串比较使用strcmp

它们读入:t4_26

string str;

cin >> str;

size_t str_size = 90;

char ch[str_size];

cin >> ch;

13. 创建动态数组, 在运行时确定长度

int *p = new int[3];  //内置类型分配空间 无初始化

int *p = new int[4](); 值初始化为0, 但没有初始化值列表的形式

const int *p = new const int[22](); //最后的()是必须的,不然就没有初始化const元素 ,虽然vs2005通过

string *pstr = new string[4]; // 分配空间 使用默认构造函数初始化

const char *p4 = s2.c_str();

14. int arr[0]; // error,不能为0

int *parr = new int[0]; // ok, 可以为0,但是parr不能进行解引用操作

15. delete[] parr; // 漏掉[]编译器不会发现错,运行时错

16. 使用string比C风格字符串的程序执行速度要快很多

17. 可以使用C风格字符串对string对象赋值或初始化

C风格字符串可以作为左操作数与string对象相加

    char*ch = "hello ";

    strings("good");

    strings2;

    s2 =ch +s; //  ok

    s2= “abc” + ch + s; // error

18. 使用数组初始化vector

in iarr[6] = {0,1,2,3,4,5};

vector ivec(iarr+1, iarr+4); //[a,b),不包括iarr[4]

19.  t4_34

   vector ivec;

   string str;

   while (cin >> str) { // 读入一组string放在vector中

       ivec.push_back(str);     

    }

//下面是将vector这组string拷到一个字符指针数组中。

   char **pa = new char*[ivec.size()];

   size_t k = 0; // #include

    for(vector::iterator it = ivec.begin(); it != ivec.end(); ++it) {

       char *p =  new char[(*it).size() +1]; // 分配空间

       strcpy(p, (*it).c_str());  //拷贝 #include

       pa[k++] = p;  

    }

20.  int *p[3];

int (*p)[3];

21.  使用typedef简化多维数组

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

typedef iint int_array[4];

int_array *p; // p也可以定义为 int (*p)[4];

for (p = ia; p != ia + 3; ++p;) {

    for(int *q = *p; q != *p+ 4; ++q) {

       cout<< *q << endl;

}

}

第五章表达式

1.   指针不能转化为浮点类型

2.   short             [-32768,32767]

unsigned short:      [0, 65535]

    int:                 [-21.4亿, 21.4亿]

3.   -21 % -8 值为 -5

4.   对于位操作,由于系统不能确保如何处理符号位,所以建议使用unsigned数!

 

左移补0

unsigned char ch = 0277;

ch = ~ch; 全部取反 flip操作

ch << 1; // 左移,右边补0

       //右移左边补0; 右移个数要小于8(操作数的位数)

对于signed,右移补0还是符号位根据机器而定

 

bitset优于整数数据的低级直接位操作

bitset_quiz1.set(27);

int_quiz1 |= 1UL << 27;

bitset_quiz1.reset(27);

int_quiz1 &= ~(1UL << 27);

<<优先级高于<

所以 cout << (10 < 32); // ok

     cout << 10 < 32;   // error

 

5.    i + j = k; // error

int i;

const int ci = i; // ok, 将来ci无意义

6.    int a; int *p;

a = p = 0; // error 不能将指针类型赋给int

7.    a += b; a计算了一次, a = a + b; a计算了两次

8.    使用++i而不用i++:

工作量少啊,后置操作要先保存操作数原来的值,以便返回未加1之前的值作为操作的结果。

9.    .>*  取成员操作大于解引用操作

(*pstu).name = “leiming”;

 pstu->name = “leiming32”;

++>*  ++也大于解引用操作

*iter++ 相当于 *(iter++)

10.  t5_18:定义一个string指针的vector,输入内容,然后输出

       vector vec;

    string str;

    cout << "enter strings:" ;

    while (cin >> str) {

          string *pstr = new string; // 必须新分配一块地方

                     // string *pstr = &str;不对,指向同一空间啦

          *pstr = str; // 每个指针单独指向一块空间,赋值

        vec.push_back(pstr);

        if(vec.size()==4) cout <<"vec[1]:" << *vec[1] << endl;

    }

    for (vector::iterator it =vec.begin(); it != vec.end(); ++it) {

        cout <<**it << ", len: " << (**it).size()<< endl;

}

11. 三种语法:

sizeof (expr)

sizeof (type name) // 必须加括号

sizeof expr :  并没有计算表达式的值

Sales_item item, *p;

sizeof item;

sizeof *p; // 并没有解引用操作,  p指向一块没有意义 的地址

sizeof (Sales_item)

12. 逗号表达式的是其最右边表达式的值

13.  int *p = new int; //ok 没有初始化

int *p = new int();// okp所指的对象内容初始化为0

 

int *p = 0;

delete p; // ok,可以删除空指针

 

delete p;之后,删掉了它所指向的对象,p此时成了悬挂指针,应该p = 0;

 

如果你使用delete是未加括号,delete便假设删除对象是单一对象。否则便假设删除对象是个数组。delete[] 相比 delete 会多做一件事,就是在删除这个内存块前,析构每个元素,特别是对于对象数组

万一需要重载 new, delete,new[], delete[], 我们必须全部都重载,而不能仅仅重载 new, delete, 然后希望 new[],delete[] 会调用它们

 

string*stringPtr1 = new string;
  string *stringPtr2 = newstring[100];
  ……
  delete stringPtr1;
  delete [] stringPtr2;
  如果你对着stringPtr1使用“[]”形式,其结果未定义。如果你对着stringPtr2没有使用“[]”形式,其结果亦未定义。犹有进者,这对内建型别如int者亦未定义,即使这类型别并没有destructors。

 

游戏规则很简单,如果你在调用new时使用了[],则你在调用delete时也使用[],如果你在调用new的时候没有[],那么你也不应该在调用时使用[]。

 

14. a[][5]; // 第一维可缺省 ,5不可省

15. 动态创建const对象:

const int *pci = new const int(32); // 定义的时候必须初始化,初始化后就不可以修改  pci指向一个const对象,所以它也要是const

1)  普通指针不能指向const对象;

void reset(int *pi) //实参不能是指向const int对象的指针

 // 为了保护所指向的值,一般地将定义为指向const对象的指针

void reset(const int *cpi);

2)  指向const对象的指针可以用普通指针来初始化

3)  指向const对象的指针可以指向非const对象,但不能修改它,可以赋给非指向const对象的指针来改;

4)  可以使用普通指针初始化const指针

5)  不能用const对象来初始化非const的引用

6)  可以使用const对象来初始化非const对象,反之亦然

所以const形参的时候,实参可以是const对象也可以是非const对象

C语言中,const形参与非const形参没有区别,为了支持与C的兼容,编译器将const形参看作是普通的形参,所以const不能看作是函数重载的根据

 

const string *pcs = new conststring; // ok

内置类型的以及没有默认构造函数的类型必须显式初始化

 

可以删除const指针:

delete pci; // ok

delete pcs; // ok

16. t5_30:

vector svec(10); // 十个空串元素

vector **pvec = new vector[10]; // error

    //右边返回的是一个指向元素类型是vector的指针

17. 算术转换:

char, signed char, unsigned char, short, unsignedshort如果能包容在int内则转换为int,否则转成unsigned int(主要针对unsiged short)

 

bool 提升为int, false转为0, true转为1

    inta = 2 + true;

    cout<< a << endl; // 3

 

 向上提升:int->unsigned int->long->unsignedlong

signed int + unsigned int,则signed int会转为unsigned int

    unsignedchar b = -1; // 1000 0001,符号位不变所有位取反,再加1 : 1111 1111

    cout<< (int)b << endl; // 255

 

    inti = 32;

    constint &iref = i; // 使用非const对象初始化const对象的引用,将非const对象转成const对象

       i= 33;

    //iref= 34; // error

    cout<< iref << endl; //ok, iref要随着i而变 = 33

const int *cpi = &i; // 非const对象的地址(或非const指针)转成指向相关const类型的指针

int *pi = &i;

const int *cpi2 = pi; // 可以使用非const指针初始化const指针

 

 

18.  显式转换

1)   什么时候使用

       i.     强制转换

      ii.     存在多种类型转换时,需要选择一种特定的类型转换

2)   cast-name(expression)

dynamic_cast

运行时识别指针或引用所指向的对象

3)   const_cast

    int*cpi3 = const_cast<int *>(cpi);// 是指针或对象

    //*cpi= 100; // error

    *cpi3= 43;

    cout<< "i=" << i << endl;

 

       const int ci = 32;

    int ival = const_cast<int>(ci);//error不能从const intint

    int &iref2 = const_cast<int&>(ci); // 可以将const转换掉

    iref2 = 35;

    cout << ci << endl; // 32

    cout << iref2 << endl; // 35

4)   static_cast

int ival = static_cast(2.4); // 关闭警告,不关心精度丢失

 

可以找回放在void*中的地址值

double dval = 2.33;

void *p = &dval;

double *pd = static_cast(p);

5)   reinterpret_cast

为操作数的位模式提供较低层次的重新解释,依赖于机器,要阅读编译器细节。

int *ip;

char *pc = reinterpret_cast(ip);//不能把pc看成是普通字符指针,程序员必须记住pc指向真正指向的是int

string str(pc); // compile error

6)   最后:避免使用强制类型转换,也能写出好程序

使用const_cast抛弃cast属性意味着设计缺陷

有些强制转换有危险

如果必须要用,则限制强制转换值的作用域,记录所有假定涉及的类型

7)   在新编译器上写程序,最好不要用()式的老强制转换

19.  dval = ui * fval; // ui先转成float再乘,结果是double

你可能感兴趣的:(Programming,LanguageC/C++)