我是靠谱客的博主 健康纸飞机,最近开发中收集的这篇文章主要介绍《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<string>

usingstd::string;

   using std::cout;

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

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

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

打开vs2005命令提示

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

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

要加入程序变量: simple < databook_sales

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

5.         

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

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

 

char ch[20];

       cin.getline(ch,5); 

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

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

char ch[20];

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

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

 

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

        #include <string>
        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<iostream>

与getline(cin,str)类似

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

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

    constchar *st = "The expense of spiritn";

    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 <cctype>  p77页

isalnum(c)  字母数字

isalpha(c) 字母

iscntrl 控制字符

isdigit 数字

isgraph 不是空格但是可以打印

islower 小写

isprint 可打印

ispunct 标点

isspace 空格

isupper 大写

isxdigit 十六进制数

tolower 变小写

toupper

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

12. string s;

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

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

ispunct(ch)

result_str+= ch;

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

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

vector<int>v3(10,5); // 10个5

 

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

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

 

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

 

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

 

vector<int>ivec(10); // 10个0

for(vector<string>::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<int>::iterator

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

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

bitset<n> b(s); // 字符串

bitset<n> 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 <cstddef>

//一个与机器相关的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<int> 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 <cstdlib>

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

c)   void *

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

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

真正的空指针是说,这个指针没有指向一块有意义的内存,比如说:
char* p;
这里这个p就叫空指针.我们并未让它指向任意地点,又或者 char* p = NULL;这里这个p也叫空指针,因为它指向NULL 也就是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 <cstddef>

    // 与机器相关: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<void*>(const_cast<string*>(ps));

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

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

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

对于void *pv;

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

或者pc =static_cast<char*>(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 <cstring>

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<int> ivec(iarr+1, iarr+4); //[a,b),不包括iarr[4]

19.  t4_34

   vector<string> 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 <cstddef>

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

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

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

       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<string *> 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<string *>::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<string> svec(10); // 十个空串元素

vector<string> **pvec = new vector<string>[10]; // error

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

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<type>(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<int>(2.4); // 关闭警告,不关心精度丢失

 

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

double dval = 2.33;

void *p = &dval;

double *pd = static_cast<double*>(p);

5)   reinterpret_cast

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

int *ip;

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

string str(pc); // compile error

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

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

有些强制转换有危险

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

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

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

最后

以上就是健康纸飞机为你收集整理的《C++Primer》学习笔记(1-5章)第一章 文件头及声明第二章 变量和基本类型第三章标准库类型第四章数组和指针第五章表达式的全部内容,希望文章能够帮你解决《C++Primer》学习笔记(1-5章)第一章 文件头及声明第二章 变量和基本类型第三章标准库类型第四章数组和指针第五章表达式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部