C++11 学习笔记(持续更新)

今天是2021年的第一天,立个新年的第一个flag,要在1月1日~2月15日过一遍《C++ Primer Plus》和《Effective C++》,并做好笔记,写好blog,打好C++的基础

C++ primer Plus

chap4 复合类型

1.数组、array 、vector

看了4.1和4.10 ,这两节主要讲的知识点如下:

  • 数组的声明创建和初始化
    数组的声明的形式为:typeName arrayName[arraySize]; 其中arraySize指定元素数目,必须为整型常数或const值,也可以是常量表达式(如8*sizeof(int)), 即其中的值在编译时必须是已知的,不能为变量(变量的值在程序运行时设置的)。
  • 数组的初始化规则
    只有在定义数组时才能使用初始化,以后就不能使用了,也不能将一个数组赋给另外一个数组。然而可以使用下标分别给数组赋值。初始化数组时,提供的值小于数组的元素个数时,则编译器将把其它元素设置为0。
int c[4] = {
    
    2,3};    //ok
int hard[4];         //ok
hard[4] = {
    
    2,3};     //not allowed
hard = cards;        //not allowed
hard[1] =3;          //ok

通常,让编译器计算元素个数是一种非常糟糕的事情,但这种方法对于将字符数组初始化为一个字符串来说比较安全

char dog[] = {
    
    'a' ,'b' ,'e'}
short thing[] = {
    
    1,3,4}
  • C++11对列表初始化新增了三个功能
1.初始化数组时,可以省略等号(=)
double earnings[3] {
    
    1.2e4,1.6e3,1,8e5};

2.可不在大括号内包含任何东西,把所有元素设置为0
unsigned int counts[10] = {
    
    };  //all elements set to 0
float balance[100] {
    
    }; //all elements set to 0

3.列表初始化禁止缩窄转换(比如将浮点数转化为整型)
long plifs[] = {
    
    23,93,9.0};  //not allowed 因为9.0为浮点数,需要转化为long整型
char slifs[4] {
    
    'h','i',12222222,'\0'};  //not allowed 因为12222222超过了char变量的取值范围
char tlifs[4] {
    
    'h', 'i', 112,'\0'};  //allowed 112虽然是一个int值,但它在char变量的值范围内
  • 模板类vector
    vector 是动态数组,可以在运行阶段设置vector对象的长度,是使用new创建动态数组额的替代品。vector也包含在名称空间std中
#include <vector>

using namespace std;
vector<int> vi;   //create a zero-size array of int
int n;
cin>>n;
vector<double> vd(n);  //create an array of n doubles

vector vt(n_elem) ,其中n_elem可以是整型常量也可以是整型变量

  • array
    vector功能强大,但效率稍低。有鉴如此,c++11新增了模板类array,也位于名称空间std中。与数组一样,array的对象长度也是固定的,也使用栈(静态内存分配),而不是自由存储区。
    array<typeName, n_elem> arrName;
    n_elem不能是变量,在c++11中,可以用列表初始化vector和array对象
#include <array>

using namespace std;
array<int,5> ai;   //create array object of 5 lines
array<double,4> ad = {
    
    1.2, 2.3, 3.2, 3.4};
 
array<double,4> ai;
ai  = ad ;     //ok
  • 比较
1.数组、vecotr 、array都可以使用标准数组表示法来访问各个元素
2.从地址可知,array对象和数组存储在栈中,而vector对象存储在另一个区域中(自由存储区或堆)
3.可以将一个array对象赋给另一个array对象,而对于数组,必须逐元素复制数据
  • 有效下标的重要性
1.在数组、vector、array对象中,如果数组下标超界,编译器不会报错,但会赋值给某个内存位置,造成程序的其它问题。
double a[10];
a[-2] = 10.0;   //转化为 *(a-2) = 10.0 ,其含义如下:找到a指向的地方,向前移两个double元素,并将10.0存储到目的地

2.vector和 array可以用成员函数at()避免这种情况, at()函数时将在运行期间捕获非法索引
array<int ,2>a2;
vector<int>a3;
a2[-2] = 5; //still allowed,但不安全
a3[-2] = 4; //still allowed,但不安全

a2.at(1) = 2; //assign 2 to a2[1]

a2.at(-2) = 5;  //not allowed
a3.at(-2) = 4;   //not allowed

2.字符串

看了4.2和4.3,知道字符串就是存储在内存的连续字节中的一系列字符,C++处理字符串的方式有两种,第一种是C-风格字符串,第二种是基于基于string类库的方法。主要的知识点如下:

2.1 C-style string

1.char数组里存储,但结尾必须是空字符('\0',其ASCII为0)
char dog[3] = {
    
    'w', 'e', 'd'};  //not a string
char cat[3] = {
    
    'e', 'e', '\0'}; // a string


2.字符串常量(字符串字面值):使用双引号括起来
char bird[11] = "Mr Cheeps";   // the \0 is understood
char fish[] = "Bubbles";       // let the compiler count

//注意:字符串常量(使用双引号)不能与字符常量(使用单引号)互换
char sh = 'S';   //this is a char 
char sh = "S";   // illegal type mismatch  ,因为这个”S“不是字符常量,表示的是两个字符(字符\S和\0)组成的字符串。\\
                 //而且是”S“实际上表示的是字符串所在的内存地址,而地址在C++中是一种独立的类型,因此C++编译器不允许这种不合理的做法。

下面介绍C-syle string 的另几个知识点

1.拼接字符串常量
有时候,字符串很长,无法放到一行中,C++允许拼接字符串常量。任何两个有空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接成一个,下面两个输出都为”dddhhhh“.
cout<<"ddd" "hhh";  
cout<<"ddd"
"hhh";   

2.在数组中使用字符串:将数组初始化字符串常量 或 将键盘或文件输入读入到数组中。标准头文件cstring(老式为string.h)提供了strlen函数,该函数只计算可见的字符,而不把空字符计算在内。

const int Size =10;
char name1 [Size];
char name2 [Size] = "C++owboy";
cin>>name1;  

3.字符串输入
3.1 cin
cin使用空白(空格、制表符、换行符)来确定字符串的结束位置,这意味者在获取字符数组输入时只读取一个单词。
读取该单词后,cin将该字符串放到数组中,并自动在结尾添加空字符。所以cin无法读取空格
另一个问题是无法避免输入字符串可能比目标数组长
3.2 每次读取一行字符串的输入
1) getline() :读取一行输入,直到到达换行符,随后在队列中丢弃换行符
cin,getline(name,20);  //最多读取19个字符,最后一个字符留给’\0‘

2)get(): 读取一行输入,直到到达换行符,但在队列中保留换行符
这就出现一个问题;假如连续两次调用get(),由于第一调用,换行符留在输入队列中,第二次调用时看到的第一个字符便是换行符。
因此get()认为已到达行尾,而没有发现任何读取的内容。避免这种情况,就需要使用下面这种方式:
cin.get(name,20).get();  //第二个get是专门用来读换行符的
cin.get(name1,20).get();

get()比getline的好处: 老式的没有实现getline() ; 其次,get()输入更仔细,
可以检查停止读取的原因是已经读取了整行还是由于数组已经填满。

4.混合输入字符串和数字
int year ;
cin>>year;
char address[80];
cin.getline(address,80);  

实际上用户根本没有输入地址的机会,当cin读取年份,将回车键生成的换行符留在了输入队列中,后面的getline()看到换行符后,被认为是一个空行,并将空字符串赋给address。解决办法:在读取地址之前先读取并丢弃换行符。
int year ;
cin>>year;
cin.get();  //或者(cin>>year).get()
char address[80];
cin.getline(address,80);

2.2 指针与C-风格字符串
c++通常不用数组而用指针表示C-风格字符串
注意:在cout和多数C++表达式中,char数组名、char指针以及用引号括起来的字符串常量都被解释为字符串第一个字符的地址

char animal[20] = "bear";
const char *bird = "wren";    //将"wren"的地址赋给了bird指针。(一般编译器在内存中留出一些空间,以存储程序源代码所有用引号                         
                              //括起来的字符串,并将每个被存储的字符串与地址关联起来)
                              //字符串字面值师常量,所以用关键字const ,意味者可以用bird来访问字符串,但不能修改它
char *ps;  

//一般来说,如果给cout提供一个地址,它将打印地址。但如果指针的类型为char*,则cout将显示指向的字符串。
//如果要显示字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*,后面见
cout<< animal << "and";
cout<<bird<<"\n";       //输出为 bear and wren
//cout<<ps;             //如果显示未定义的指针,则可能显示一个空行、一堆乱码,或者导致程序崩溃

//对于输入,使用animal输入可以,但不能使用bird来输入,因为bird指针被声明为const,编译器将禁止改变bird指向的位置中的内容
//ps没被初始化,也不能用来直接输入
//请不要用字符串常量或未被初始化的指针来接收输入
cin>>animal;   //可以 ,假设读入fox
//cin >> ps;  
//cin>>bird;

ps = animal;  //不会将animal的字符串复制给ps,只会复制地址  
cout<< animal <<"at" <<(int *) animal<<endl;   //输出: fox at ox0065fd30
cout<<ps<<"at"<<(int *)ps<<endl;               //输出: fox at ox0065fd30

//获得animal字符串的副本,首先应给ps分配内存来存储该字符串,然后要用strcpy函数
ps = new char[strlen(animal)+1];   
strcpy(ps,animal);   // strcpy接受两个参数,第一个是目标地址,第二是要复制的字符串的地址
cout<< animal <<"at" <<(int *) animal<<endl;   //输出: fox at ox0065fd30
cout<<ps<<"at"<<(int *)ps<<endl;               //输出: fox at ox004301c8

//经常要把字符串放到数组中,初始化数组时,请使用=运算符;否则使用strcpy()或strncpy()
char food[20] = "carrots";   //initialization
strcpy(food, "a picnic  ddd  jjjjj weih jdddddjjhh");  //这种情况,函数将第二个参数字符串中剩余的部分
                                                       //复制到数组后面的内存字节中,可能会覆盖程序正在使用的其它内存
strncpy(food, "a picnic  ddd  jjjjj weih jdddddjjhh",19); //strncpy()第三个参数:要复制的最大字符数
food[19] = '\0';                                       //在复制完字符串加上空字符,以标记该字符串的结尾

2.3 string类
string类位于名称空间std中(引用时要不使用using编译指令或者使用std::string)。用之前要使用**#include **

1. 使用string对象的方式与使用字符数组的相同点主要有以下4char charr1[20];
char charr2[20]= "jajuar";
string str1;
string str2 = "panther";  //(1) 可以使用C-风格字符串来初始化string对象

cin>>charr1;  
cin>>str1;  //(2)可以使用cin来键盘输入存储到string对象中

cout<<charr1<<endl;
cout<<str1<<endl;   //(3)可以使用cout来显示string对象

cout<<str2[2]<<endl;  //(4)可以使用数组表示法来访问存储在string对象中

2.string对象和字符数组之间的主要区别是:可以将sring对象声明为简单变量,而不是数组, 且程序能自动处理string的大小

(1) C++11 也允许将列表初始化用于C-风格字符串 和 string对象

char first[] = {
    
    "le chapon"};
char second[] {
    
    "The Elegant"};
string third = {
    
    "The Bread"};
sring fourth {
    
    "Hank Fine"};

(2) 赋值、拼接

1.赋值 ,不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象; C-风格字符串可以用strcpy()函数(在头文件cstring中)

char charr1[20];
char charr2[20]= "jajuar";
string str1;
string str2 = "panther";
charr1 = charr2;   //INVALID
str1 = str2 ;      //VALID
strcpy(charr1, charr2);   //copy charr2 to charr1

2.拼接, string类: 可以用+或者+=将对象拼接起来 ; C-风格字符串可以用strcat()函数(在头文件cstring中)。
  拼接是strcat()函数有可能越界,但string类因为自动计算大小就不会
  
string str3;
str3 = str1 +str2;  //assign str3 the joined strings
str1 += str2;      //add str2 to the end of str1

strcat(charr1, "juice");  //add juice to end of charr1

3.计算字符串长度
int len1 = str1.size();     //获取string对像的长度 
int len2 = strlen(charr1);  //获取C-风格字符串的长度

(3)string类I / O

string类和C-风格字符串都可以用cin来读取一个个单词和cout来输出,用法相同。但读取一行时,用法不同

char charr[20];
string str;

cout<<strlen(charr)<<endl;  //未被初始化的数组,空字符的位置是随机的,有可能是12,13等等
cout<<str.size();           //未被初始化的字符串,长度自动为0

cin.getline(charr,20);
getline(cin,str);  //getline()函数包含在iostream头文件中

(4)其它形式的字符串字面值

1.C++有类型wchar_t ,C++11 新增了char16_tchar32_t 。c++分别用前缀L、u、U来表示
wchar_t title[]= L"chief Astrogator";  //w_char string
char16_t name[] = u"Felonia Ripova";   //char_16 string
char32_t car[] = U"Humber Super";      //char_32 string

2.C++11新增了另一种类型是原始(raw)字符串。
(1)默认定界符R"(原始字符串)"  ,那么原始字符串中,字符就表示自己,例如输出“ 就不需要家转义字符\""。
cout<<R"(Jim "King”  \n instread of endl)"<<'\n';  //输出为:Jim "King”  \n instread of endl2)自定义定界符:是为了解决原始字符串中包含)", 形式:R" 任何字符(原始字符串)任何字符"  ,但任何字符不能包括空格、左右括号、斜杆和控制字符(如制表符和换行符)
cout<<R"+**(”(Who wouldn't ?)",she whispered.)+**"<<'\n';  //输出为:”(Who wouldn't?)",she whispered.

3.结构体

1.结构的定义、创建、初始化、成员的访问。

1.结构的定义、创建
struct inflatable {
    
    
     char name[20];
     float volume;
     double price;
}guest;             //创建方式之一

inflatable pal;    //创建方式之二

2.结构的初始化:C++11支持将了列表初始化用于结构,且等号(=)是可以选的
inflatable mayor = {
    
    "dddd", 0.12, 9.98};

3.结构成员的访问:可以用成员运算符(.)来访问各个成员
cout<< mayor.name;  // dddd

2.结构的其它属性

1.接受string类作为成员
struct inflatable {
    
           
     std::string name;   //      
     char name[20];      
     float volume;      
     double price; 
 };
2.结构可以作为函数的参数和返回值

3.结构可以用“=”进行赋值
inflatable choice;
choice = mayor;

4.可以声明没有名称的结构体,但以后无法用上述的第二种方式创建结构体变量
struct  {
    
    
     char name[20];
     float volume;
     double price;
}guest;

3.结构数组:可以创建元素为结构的数组

struct inflatable {
    
    
     char name[20];
     float volume;
     double price;
};
 
inflatable gifts[10];   //结构数组

//结构数组的初始化:初始化数组的规则+初始化结构的规则
inflatable guests[2] = {
    
    
     {
    
    "bami",0.5,21.99},
     {
    
    "Dooo",20.3, 78.999}
};

//结构数组的访问:
cout<<guests[0].name;

4.结构中的位字段
C++11允许指定占用特定位数的结构成员,这使得与创建某个硬件设备上的寄存器对应的数据结构非常方便,但通常用于低级编程中,一般来说,可以试用整形和按位运算代替这种方法

struct  {
    
          
      unsigned int SN :4;      // 4 bits for SN vlaue
      bool goodIn: 1;          //valid input(1 bit)
 };

4.共有体(union)

共用体(union) 是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。共用体每次只能存储一个值,因此必须要有足够的存储空间来存储最大的成员,所以,共用体的长度为其最大成员的长度。公用体的句法与结构很相似,但含义不同,例如结构可以同时存储int 、doubel和long,但共用体只能用它们其中最大的一个元素大小的内存空间存储int、long 或 double。

union one4all
{
    
    
    int int_val;
    long long_val;
    double double_val;
};

one4all pail;
pail.int_val = 15;  //store an int
pail.double_val = 1.30;  //store a double ,int value is lost
1.共用体的用途: 但数据项使用两种或更多格式(但不会同时使用),可节省空间。 
   所以常用于嵌入式系统编程,如控制烤箱和火星漫步者的处理器。
   下面假设管理一个小商品目录,其中有一些商品的ID为整数,但另一些的ID为字符串,在这种情况下,可以这样做:
   struct widget{
    
    
      char brand[20];
      int type;
      union id{
    
    
          long id_num;
          char id_char[20];
      }id_val;
    ...
  };
widget prize;
if(price.type==1)
    cin>>price.id_val.id_num;
else 
    cin>>price.id_val.id_char;


2.anonymous union(匿名共用体)没有名称,其成员将成为位于相同地址处的成员变量,显然每次只有一个成员是当前的成员
 struct widget{
    
           
      char brand[20];       
      int type;       
      union {
    
               //anonymous union   
          long id_num;           
          char id_char[20];       
       };     ...
  }; 
  widget prize; 
  if(price.type==1)     
       cin>>price.id_num;   //anonymous union中的成员直接被视为prize的成员
  else      
       cin>>price.id_char;    

5.枚举(enum)

C++ 的enum 工具提供了另一种创建符号常量的方式,这种方式可以替代const。它还允许定义新类型,但必须按照严格的限制进行。选择多少空间来存储枚举右编译器来决定,对于取值范围较小的枚举,使用一个字节或更小的空间;而对于包含long类型值得枚举,则使用4个字节。

//enum 定义

enum spectrum {
    
    red, orange, yellow,green,blue,violet,indigo,ultraviolet};
//spectrum成为一种新类型的名称,被成为枚举enumeration
//将red 、orange、yellow等作为符号常量,默认情况下对应的是0~7

//可以用枚举名来声明这种类型的变量,声明的变量只能赋值枚举里的
spectrum band;  
band = blue;   //valid
bane - 2000;   //invalid
band = orang;  //valid ,枚举只定义了赋值运算
++band;        //invalid
band = orange + red;   // invalid,but a little tricky  //此时orange + red 转化为1+0, 这是一个合法得表达式,
                       // 但其类型为int 不能将其赋给spectrum的变量band

int color = blue;
band = 3;    //invalid , int not converted to spectrum
color = 3+ red;  //valid ,red converted to int

 band = spectrum(3);   // 强制类型转化, typecast 3 to type spectrum
 band = spectrum(40003);  //undefined ,因为40003超出了枚举的取值范围
  • 设置枚举量的值
enum bits {
    
    one =1, for = 4,eigth =8};  //指定的值必须为整数
enum bigstep {
    
    first,second = 100,third}; //此时third 为101
enum {
    
    zero,null=0.one, numero_uno=1};    //枚举可以创建多个值相同的枚举量,其中zero和null都为0, one 和numro_uno都为1
  • 枚举的取值范围
枚举的取值范围: 在强制类型转化中,可以将取值范围内的任何整数值赋给枚举变量
上限: 大于枚举量的最大值的最小的2的幂-1,例如 最大值为101,大于这个值最小的2的幂为128,则上线就为127
下限: 如果枚举量的最小值不小于0, 则下限就为0;  小于0, 采用寻找上限同样的方法,但加上负号。例如 最小枚举量为-6 ,则比它小的、最大的2的幂是-8,因下限是-7.

enum bits{
    
    one =1, two =2, four = 4, eight=8};
bits myflag;

myflag = bits(6) //合法, bits的取值范围为[0,15]

6.指针和自由存储空间

计算机在存储数据时必须跟踪的3种基本属性:(1)信息存储在何处 ; (2)存储的值为多少;(3)存储的信息是什么类型
定义一个简单变量,声明语句指出了值的类型和符号名,还让程序为值分配内存,并在内部跟踪该内存单元。
而C++在开发类时有另一种策略:指针。面向对象编程(OPP)与传统的过程性编程的区别在于,OPP强调的是在运行阶段,而不是编译阶段。运行阶段指的时程序在运行时,编译阶段指的是编译器将程序组合起来时。而指针在OPP阶段很重要,比如指针使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。

  • 6.1 声明和初始化指针
1.指针是一个变量,存储的是值的地址,而不是值本身

2.指针声明必须指定指针指向的数据的类型,如下。指针名表示一个地址,*运算符被称为间接值或解除引用运算符,将其应用于指针,
  可以得到该地址处存储的值;有时候也为乘法,这将根据上下文判断。
int* p;  //C++声明法,在C++中,int* 是一种复合类型,是指向int的指针
int *p;  //C程序员
int*p;   //也可以这样做
int* p1, p2;  //声明创建一个指针(p1) 和 一个int变量(p2)

3.可以在声明中初始化指针,被初始化的是指针,而不是它指向的值
int higgens =5;
int* pt = &higgens;
  • 6.2 指针的危险
    一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址
long* fellow;  //create a pointer-to-long
*fellow = 23333; //place a value in never-never land

上述的行为很危险,由于fellow没有初始化,它可能是任何值,不管值是什么,程序都将它解释为存储23333的地址,
如果fellowd的值为1200,计算机将把23333这个数据放在地址1200上,而1200可能程序代码的地址,这就会出现错误。
所以极其重要的一点:在C++创建空间时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存,
为数据提供空间时一个独立的步骤

注意:但用new创建动态数组时可以
long* p = new long;
*p = 5;  //在自由内存区:堆或自由存储区开辟出一块内存,存储值为5的值,并将此内存地址返回给存储在栈内的p
  • 6.3 指针与数字
1.指针不是整型,虽然计算机通常把地址当作整数来处理。整数可以执行加、减、除等运算地数字,而指针描述地是位置
  所以不能简单地将整数赋给指针,而要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当地地址类型。
  int* pt;
  pt = 0xB8000000;  // type mismatch
  pt = (int*) 0xB8000000  /type now match
  • 6.4 使用new给数据对象(可以是结构也可以是基本类型)分配内存
    通用格式:
    typeName pointer_name = new typeName
    delete pointer_name
1.指针用new声明时,最大的用处是在运行阶段分配未命名的内存以存储值。需要注意以下几点:
(1new int 告诉程序,需要在堆(heap)中或自由存储区(free store)中分配适合int的内存,当然这个堆内存还没分配,如下

 int *p = new int;     // new分配的内存是在堆中,可以在程序运行时在分配,不需要在编译阶段分配
 cout<<&p<<endl;       //  0x7ffe18027710 ,栈内存
 cout << p << endl;    //  0x1891e70 ,这个地址表示heap或free store中的地址
 cout << *p << endl;   // 02)普通数据对象存储在栈内,而new创建的对象存储在堆中。
 int x =4;
 *p = 4;              //在堆地址0x1891e70申请内存,存储值4
 cout<<&x<<endl;     //0x7ffe1802770c ,栈内存
 cout << p << endl;  //  0x1891e70 ,这个地址表示heap或free store中的地址 
 cout << *p << endl;  //4     
 
 (3)在堆中用完内存后,需要用delete 释放,实质是释放p里存储的堆中内存,但在栈中的p指针仍然保留。
 delete p;      
 cout << p << endl;      // 0x1891e70 ,这个地址表示heap或free store中的地址
 cout << *p << endl;     //04delete 只能删除在堆中的内存;且一般不能删除同一个内存块两次,所以不要创建两个指向同一内存块的指针,
     但对于返回指针的函数,使用另一个指针确实有道理
 int jugs =5; 
 int* pi = &jugs;  
 delete pi; // not allowed ,memory not alloccated by new
 int *ps = new int;
 int* pq = ps;
 delete pq;    //ok, 实际上删除的是在堆中的内存
  • 6.5 使用new来创建动态数组
    使用new时,如果在运行阶段需要数组,则创建它;如果不需要,则不创建,还可以在程序运行时选择数组的长度,这被称为动态联编(dynamic binding)
    通用格式: type_name* pointer_name = new type_name [num_elemets]; 其中num_elelments可以为变量
    删除时: delete [] pointer_name;
    使用new和delete时,应遵循以下规则:
    (1)不要使用delete来释放不是new分配的内存;
    (2)不要使用delete释放同一个内存块两次;
    (3) 如果使用new[] 为数组分配内存,则应使用delete[] 来释放
    (4) 如果使用new[] 为一个实体分配内存,则应使用delete(没有方括号)来释放
int* p = new int[3];
p[0] = 3;
p[1] = 2;
p[2] = 1;
cout<<p[1]<<endl;  //2
p = p+1;
cout<<p[0]<<endl;  //2
p = p-1;     // point back to beginnig ,给delete[] 提供正确的地址
delete [] p;
  • 6.6 指针小结
(1) 声明指针、指针赋值和删除指针
double* pn = new double;
double* pa = new int[10];

*pn = 4.3;
pa[0] = 2.2;

delete pn;
delete [] pa;

(2)对指针解除引用意味者获得指针指向的值
cout<<*pn<<endl; //获取指针pn所指向的值,*pn完全等同于一个double类型的变量
cout<<*pa<<endl; //*pa相当与pa[0]

(3)动态联编时的数组和静态联编时的数组
使用数组时声明来创建数组时,将采用静态联编,即数组的长度在编译时设置;
使用new[]运算符创建数组时,将采用动态联编(动态数组),即在运行时为数组分配空间。

int a1[3] = {
    
    5,2,3};
int n;
int* a2 = new a2[n];
cin>>n;  //输入为3
a2[0] = 1;
a2[1] = 3;
a2[2] = 8;

数组名和指针名大多数情况下,都可以互通,它们都表示地址。低于它们,可以使用数组方括号法也可以使用解除引用运算符(*)。
它们之间有两个区别:
(1) 可以修改指针的值,但数组名是常量
a1 = a1 +1;  //not allowed
a2= a2 + 1; //valid
(2)对指针和数组使用sizeof,指针得到的是指针名,而数组得到的是数组的长度
cout<<sizeof(a1)<<endl; //3*4 ,表示数组长度*int占得字节大小
cout<<sizeof(a2)<<endl; //表示地址占的字节大小,一般为4个字节

Effective C++

猜你喜欢

转载自blog.csdn.net/weixin_37617732/article/details/112072667
今日推荐