学习C++ primer总结(一到六章)

        本人最近学习C++primer,顺带记录自己的学习。

第一章 开始

1.1编写一个简单的C++程序

        每个C++程序都包含一个或多个函数,其中一个必须命名为main函数。

1.2认识输入输出

对象用途
cin标准输入
cout标准输出
cerr标准错误
clog用来输出程序运行时的一般性信息

1.3注释简介

        两种注释:

        单行注释://

        多行注释:/*(注释内容)*/

1.4控制流

        while:一般适用于不知道循环次数

        for:一般适用于知道循环次数的;

        if:条件判断

1.5类简介

        没找到资源,所以看着比较累,不建议看

第二章 变量和基本类型

2.1基本内置类型

类型转换

        建议不要混用带符号类型和无符号类型。

字面值常量

        一个形如42的值被称作字面值常量,这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型(书中原话)。

 转义字符

2.2变量

变量定义

        基本形式:类型说明符,随后紧跟着一个或者多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。

初始值

        初始化和赋值是两个完全不同的的操作,初始化的含义是创建变量的时候赋予一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来代替。

默认初始化

        如果定义变量没有指定初始值,则变量被默认初始化。

        ****特殊情况:定义在函数体内部的内置类型变量将不被初始化,其值是未定义的,建议初始化每个内置类型的变量。

//示例
int aa = 0 , bb , cc;
double dd = 3.14;
//列表初始化,用花括号{}初始化变量
int units_sold = {0};
int units_sold{0};

变量的声明和定义

        变量的声明:规定了变量的类型和名字

        变量的定义:除了声明之外,还需要申请内存空间

        如果想声明一个变量,而非定义它,需要使用extern关键字

        ****变量能且只能被定义一次,但是可以被多次声明。

extern int i ;   //声明i,在全局中声明,在函数中定义
int j;           //声明并定义

标识符

        由字母、数字、下划线组成,其中必须以字母或者下划线开头,对大小写敏感。

 名字与作用域

        作用域:C++中大多数作用域都用花括号分隔。

        作用域中一旦声明了某个名字,它所嵌套的所有作用域都能访问改名字。同时允许在内层作用域中重新定义外层作用域中已有的名字。

        ****如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量。

#include<iostream>
using namespace std;
int i = 10;   //全局global
int main()
{
    int i = 5;  //局部local
    cout<<"i="<<i<<endl;   //使用的是局部变量
    cout<<"::i="<<::i<<endl;  //使用的是全局变量
}

2.3复合类型

        定义:复合类型是基于其他类型定义的类型。

引用(必须初始化):为对象起另外一个名字。

        ****引用本身不是对象,所以不能定义引用的引用

        ****引用要和绑定的对象严格匹配

        ****引用类型的初始值,必须是一个对象

         ****左值引用和右值引用的问题(本文未提及)(这里的引用是值左值引用)

指针

        指针本身就是一个对象。允许对指针赋值和拷贝。指针无须在定义的时候赋值。

(1)利用指针访问对象

        如果一个指针指向了一个对象,则允许使用解引用符(*)来访问对象。

(2)空指针 nullptr(C++11的新标准)

(3)void * 指针

        可以用于存放任意对象的地址,但不能对void *指针解引用,需要强转为特定类型才可以解引用。

理解复合类型的声明

(1)指向指针的指针

        **表示指向指针的指针

        ***表示指向指针的指针的指针

(2)指向指针的引用

        不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。

2.4const限定符

const用于定义一个变量,它的值不能被改变。const对象必须初始化。

        默认状态下,const对象仅在文件内有效。当多个文件出现了同名的const变量时,等同于在不同文件中 分别定义了独立的变量。 如果想让const变量在文件间共享,则使用extern修饰。

(1)const的引用

        允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式。一 般引用的类型必须与其所引用对象的类型一致,特殊情况是表达式。

        补充概念

 (2)指针和const

        ****所谓指向常量的指针或者引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,自觉的不去改变所指对象的值。

常量指针  const 数据类型 * 变量名;  不能通过解引用的方法修改内存地址的值

        ****指向的对象可以改变

        ****一般用于函数的形参,不希望在函数中修改内存地址的值

        ****如果形参的值不需要改变,建议加上const修饰,增加 程序的可读性

指针常量  数据类型 * const 变量名;    指向的对象不可改变。

        ****在定义的时候必须初始化,否则没有意义;

        ****可以通过解引用的方法修改内存地址的值(有没有感觉和引用的功能差不多

常指针常量  const 数据类型 * const 变量名;

        既不能改变指向的对象,也不能修改内存地址的值。

(3)顶层const(感觉没必要,更绕了)

        top-level const 表示指针本身是个常量

        low-level const表示指针所指的对象是一个常量。

 (4)constexpr和常量表达式

        常量表达式是指值不会改变,并且在编译过程就能等到计算结果的表达式。

        C++新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达 式。

2.5处理类型

类型别名

(1)使用关键字typedef

 (2)别名声明

        using SI = Sales_item;    //SI是Sales_item的同义词

auto:让编译器通过初始值来推断变量的类型

decltype:选择返回操作符的数据类型。只等到数据类型,不实际计算表达式的值

        语法:decltype(expression) var;

        1)如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符。

        2)如果expression是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void *)。

        3)如果expression是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是expression的引用。

        4)如果上面的条件都不满足,则var的类型与expression的类型相同。

 2.6自定义数据结构

数据结构

struct 结构体名

{

   //变量

};

        数据结构是一组相关的数据元素组织起来,然后使用它们的一种策略和方法

        C++11中支持在结构体中有成员函数,但不建议这么使用。

        C++11中,定义结构体变量可以不写struct关键字

预处理

        #idndef  SALES_DATA_H

        #define  SALES_DATA_H

        #endif

        ****一般把预处理变量的名字全部大写。

第三章 字符串、向量和数组

3.1 命名空间的using声明

        声明形式:using namespace::name;

        例如:using std::cin;

3.2标准库类型string

        #include<string>

        using std::string;     //可以用using namespace std;

①初始化

string s1;
string s2(s1);
string s2 = s1;  
string s3("value");     //直接初始化
string s3 = "value";    //拷贝初始化
string s4(n,'c');       //s4的内容是n个c

      C++标准委员会允许把字面值和字符串字面值转换成string对象。字面值和string是不同的类型。

②操作

s.empty();  //判空
s.size();   //返回字符的个数
s[n];       //第n个字符的引用
s1+s2;      //字符串的拼接
// = 赋值    == 判断相等   !=判断不等
// <, <=,> ,>=   用的可能少

补充getline函数(p87)

string buffer;
while(getline(fin,buffer))    //fin是文件指针
{
    cout<<buffer<<endl;
}

③处理string对象中的字符

        C++程序的头文件应该使用cname,而不应该使用name.h的形式,例如#include<cstring>

        函数的介绍

④基于范围的for语句(C++11)

for(declaration:expression)

      statement

3.3标准库类型vector

包含头文件#include<vector>

vector表示对象的集合,其中所有对象的类型都相同

vector是一个类模板,而不是类型(初学者可能不懂类模板的概念,不要太在意,后续学习的时候就知道了)

(1)定义和初始化vector对象

        如果用圆括号,那么提供的值是用来构造vector对象的

        如果用花括号,则是使用列表初始化该vector对象

        vector是模板而非类型,由vector生成的类型必须 包含vector中元素的类型,例如vector<int>

        这边我截了书中的内容,可以看一下

(2)向vector对象添加元素(删除)

 void push_back(const T& value) //在容器的尾部追加一个元素

void emplace_back(...) //在容器的尾部追加一个元素,用于构造元素

void pop_back(); //从容器尾部删除一个元素

先定义一个空的vector对象,在运行的时候使用push_back()向其中添加具体值

(3)其他vector操作

 结合for的范围循环

         只能对确认已存在的元素执行下标操作。

3.4迭代器的介绍

支持的基本操作:赋值(=) 解引用(*) 比较(==和!=) 遍历(++从左到右遍历)

迭代器运算符

 迭代器的类型

        容器名<元素类型>::iterator 迭代器名;

        容器名<元素类型>::const_iterator 迭代器名;

 迭代器失效的问题(补充)

        resize() reserve() assign() push_back() pop_back() insert() erase()可能会导致迭代器失效

3.5数组

        ****使用数组下标的时候,通常将其定义为size_t类型

        ****定义数组必须指定数组类型,不允许用auto推断

        ****不存在引用的数组

        ****如果两个指针指向不相关的对象,则不能进行对这两个指针进行比较

定义和初始化内置数组

 数组指针也是迭代器(还是天然的)(C++也提供了array静态数组)

经验:

①初始化可以使用memset函数

②一维数组用于函数的参数时,必须将数组长度也传进去。例void func(int *arr,int len);或者void func(int arr[],int len);

③用new动态创建一维数组:

声明语法:数据类型 * 指针 = new 数组类型[数组长度];

释放一维数组的语法:delete [] 指针;

注意:不要用delete释放同一块内存两次,第二次相当于操作野指针,所以释放内存之后将指针置为nullptr。

声明普通数组时,数组长度可以使用变量,相当于在栈上动态创建数组,并且不需要释放。

如果内存不足,调用new会产生异常,导致程序终止;如果在new关键字后面加(std::nothrow)选项,则返回nullptr,便不会产生异常,程序正常退出。

了解qsort函数

④C风格的字符串

 其他的strncpy()(有坑) 、strcpy()、strcat()、strncat()、strchr()、strrchr()、strstr()前四个使用有可能会导致数组越界。

经验:字符串在每次使用前都要初始化,减少出错的概率

vs中,要使用C标准的字符串操作函数,需要在源代码文件最上面

#define _CRT_SECURE_NO_WARNINGS

3.3.5与旧代码的接口

之前介绍过使用字符串字面值来初始化string对象,但是反过来就不成立了,所以C++提供了c_str()函数

string s("Hello World");
const char * str = s.c_str();

3.6 多维数组

        声明二维数组的语法:数据类型 数组名[行数][列数];

        行指针:数据类型 (*行指针名)[行的大小];    //行的大小即数组的长度

①将二维数组传递给函数时

        void func(int (*p)[n],int len);

        void func(int p[][n],int  len);

②多维数组

        好像没什么可以说的,理解二维数组。

第四章 表达式

4.1 基础

        重载运算符:为已经存在的运算符赋予另一种含义

        左值、右值: 当个一个对象被用作右值的时候,用的是对象的值(内容);

        当一个对象被用作左值的时候,用的是对象的身份(在内存中的位置);

        优先级:除了熟知的运算优先级,在不明确的优先级的情况,按自己想要的逻辑加括号就行。

4.2 算术运算符

注意:%参与取余运算的运算对象必须是整数类型;

4.3 逻辑和关系运算符

 逻辑与(&&)和逻辑或(||)运算符都是先求左侧运算对象的值再求右侧运算对象的值,简称短路求值

4.4赋值运算符

赋值运算符满足右结合律

不要混淆相等运算符和赋值运算符

if( i = j)
if( i == j)

4.5递增和递减运算符

递增运算符++

递减运算符--

4.6成员访问运算符

点运算符和箭头运算符

        n=(*p).size();

        n=p->size();

4.7条件运算符(三目运算)

condition? expression1 : expression 2;

如果condition为真,就执行expression1,否则就执行expression2

4.8位运算符(p137)

        有点复杂,自己看书吧!!!!!

 4.9sizeof()运算符

        sizeof运算符返回一条表达式或益哥类型名字所占的字节数,其所得值是一个size_t类型,是一个常量表达式

语法:sizeof(type) ;  (推荐版)   sizeof  expr;  (不推荐)

        对指针使用sizeof,值是定值

4.10逗号运算符

逗号运算符含义两个运算对象,按照从左向右的顺序依次求值。

4.11类型转换

隐式转换

 

显示转换
命名的强制的类型转换(p144)

        dynam_cast   只用于包含虚函数的类

        语法:派生指针 = dynamic_cast<派生类类型 *>(基类指针);

        如果转换成功,dynamic_cast返回对象的地址,如果失败,返回nullptr

****由于强制类型转换干扰了正常的类型检查,因此建议避免强制类型转换

第五章语句

5.1简单语句

(1)空语句

;     //这是一条空语句

 (2)复合语句

        复合语句是指用花括号括起来的(可能为空的)语句和声明的序列,复合语句也被称作块(block)。

{}

5.2语句作用域

        定义在控制结构当中的变量只在相应语句的内部可见,一旦语句结束,变量就超出其作用范围。

5.3条件语句

(1)if语句

(2)switch语句

        case关键字和它对应的值一起被称为case标签

        case标签必须是整型常量表达式

        如果某个case标签匹配成功,将从该标签开始往后顺序执行所有case分支,除非程序显示的中断了这一过程。

        dedault 标签:如果没有任何一个case标签能匹配上switch表达式的值,程序将执行紧跟在default标签 后面的语句。

5.4迭代语句(p165)

//(1)while语句
while(condition)
    statement

//(2)传统for语句
for(initializar;condition;expression)
    statement
//for语句中定义的对象只在for循环体内可见

//(3)范围for语句
for(declaration : expression)
    statement

//do while语句
do
    statement
while(condition)

5.5跳转语句

        break只能出现在迭代语句或者switch语句内部。仅限于终止离它最近的语句,然后从这些语句之后的第 一条语句开始执行。

        continue语句终止最近的循环中的当前迭代并立即开始下一次迭代。

        goto的作用是从goto语句无条件跳转到同一函数内的另一条语句(容易造成控制流混乱,应禁止使用)

        return

5.6try语句块和异常处理

        C++中异常处理包括:throw表达式 、 try语句块

        try 和 catch,将一段可能抛出异常的语句序列括在花括号里面构成try语句块。catch子句负责处理代码抛出的异常。

        throw表达式语句,终止函数的执行。抛出一个异常,并把控制权转移到能处理该异常的最近的catch字 句

第六章 函数

6.1函数基础

(1)形参和实参

        实参的类型必须与对应的形参类型匹配。 函数的调用规定实参数量应与形参数量一致。

(2)局部对象

        形参和参数体内部定义的变量统称为局部变量,它们对函数而言是"局部"的,仅在函数的作用域内可 见,同时局部变量还会隐藏外层作用域中同名的其他变量。

        自动对象:只存在于块执行期间的对象。

        局部静态对象:在程序的执行路径第一次经过对象定义语句时候进行初始化,并且直到程序终止才会被 销毁。

(3)函数声明

        函数的三要素:(返回类型、函数名、形参类型)。

        函数可被声明多次,但只能被定义一次。

(4)分离式编译

        分离式编译允许把程序分割到几个文件中去,每个文件独立编译。

        编译->链接

6.2参数传递

        当形参是引用类型,这时它对应的实参被引用传递或者函数被传引用调用。当 实参被拷贝给形参,这样的实参被值传递或者函数被传值调用。

(1)传值参数

(2)被引用参数

(3)const形参和实参

(4)数组形参

为函数传递一个数组时,实际上传递的是指向数组首元素的指针。

void print(const int *);
void print(const int[]);
void print(const int[10]);
//这三个函数等价
数组引用实参: f(int (&arr)[10])
int *mp[10];       //10个指针构成的数组
int(*mp)[10];      //指向含有10个整数的数组指针

(5)含有可变形参的数组

        initializer_list

        for vv(initializer_list<string> i)

6.3返回类型和return语句

return ;    //无返回值
return expression;   //有返回值

         函数完成后,它所占用的存储空间也会随着被释放掉。

        ****返回局部对象的引用是错误的;返回局部对象的指针也是错误的。

6.4 函数重载

        函数重载:如果同一作用域内的几个函数名字相同但形参列表不同(形参个数、数据类型、排列顺序);

        不允许两个函数除了返回类型不同,其他的所有要素都相同;

        重载与作用域

        如果在内作用域中声明,将隐藏(书面词)外作用域中声明的同名实体

        属于概念过渡,用模板即可

        ****使用重载函数时,如果数据类型匹配不上的话,编译器将会采用类型转换,如果转换之后有多个函数能匹配上,编译将报错

        ****重载函数有默认参数时,调用函数时,可能导致匹配失败

6.5 特殊用途语言特性

①默认实参

typedef string::size_type sz;
string screen(sz ht = 24, sz wid = 80, char background = ' ');

        函数调用时,实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参。

        ****当设计含有默认实参的函数时,需要合理设置形参的顺序。一旦某个形参被赋予了默认值,它后面的所 有形参都必须有默认值。

②内联函数 使用inline来声明内联函数

        用于优化规模较小、流程直接、频繁调用的函数

        ****运行速度可能快一点,但是占内存

        ****内联函数不能递归

③constexpr函数

        constexpr函数是指能用于常量表达式的函数。

6.6函数匹配

①确定候选函数和可选函数

②寻找最佳匹配

6.7函数指针

        函数指针指向的是函数而非对象。

void useBigger (const string &s1, const string &s2, bool pf(const string &,
const string &));
//等价于
void useBigger (const string &s1, const string &s2, bool (*pf)(const string &,
const string &));

 补充

申请内存分配失败的用法

int *a = new (std::nothrow) int [10000000000001];   //会返回空

结构体的相关知识

#pragma pack(1)    //内存对齐
struct S_struct
{
   char name[21];
   int age;
   //声明各种变量
   //C++中可以声明函数,但不提倡
};
int main()
{
    struct S_struct people;  //C++中可以不写struct
    //变量名访问结构体的变量 people.age;


    struct S_struct *pst = &people;//结构体指针
    //指针访问结构体的变量的方法:(*pst).age   或者 pst->age;

    //结构体数组
    struct S_struct peoples[3];

    

}

结构体嵌入数组

#include<iostream>
using namespace std;
struct programs
{
    char name[50];   //项目名称
    int sale[2][3] = {11,12,13,14,15,16};  //C++运行有缺省值; 结构体嵌入数组
};
void func(programs *pst)
{
    //使用结构体中的二维数组
    for(int i = 0 ; i < 2 ; i++)
        for(int j = 0 ; j < 3; j++)
            cout<<pst->sale[i][j]<<endl;  
}

int main()
{
    programs progms;
    func(&progms);
}

结构体中的指针(需要注意memset的使用)

#include<iostream>
#include<string>
using namespace std;
struct st_demo
{
    int a;
    int *p;
};
int main()
{
    st_demo demo;
    memset(&demo,0,sizeof(demo));

    demo.a=1;
    demo.p = new int[100];    //动态分配内存
    cout<<"sizeof(demo)="<<sizeof(demo)<<endl;

    cout<<"调用前:"<<"demo.p="<<demo.p<<endl;
    //memset(&demo,0,sizeof(st_demo));     //会产生内存泄漏
    memset(demo.p, 0, 100 * sizeof(int));  //清空指向内存的内容
    cout<<"调用后:"<<"demo.p="<<demo.p<<endl;
}

共同体

//声明共同体
union U_demo
{
  int a;
  double b;
};
//共同体能够存储不同的数据类型,但是,在同一时间只能存储其中的一种类型


/*
全部的成员使用同一块内存
共同体中的值为最后被赋值的那个成员的值
共同体常用于节省内存(嵌入式系统)
struct st_demo
{
    int no;
    union    //匿名共同体
    {
        int a;
        double b;
    };
};

//应用场景
//当数据项使用两种或更多种格式(但不会同时使用),可节省空间
//用于回调函数的参数(相当于支持多种数据类型)网络编程那一块

枚举

 enum colors{red,yellow,bule,other};
 colors cc = red;   //red=0的
 switch(cc)
 {
    case red: cout<<"红色。\n";break;
    case yellow: cout<<"黄色。\n";break;
    case bule: cout<<"蓝色。\n";break;
    default :cout<<"默认。\n";
}

引用即指针常量(例int *const a;)

如果引用的数据对象类型不匹配,当引用为const时,C++将创建临时变量,让引用指向临时变量。代码演示

int  main()
{
    //int &ra = 1;   编译会失败
    const int & ra = 1;   //编译没问题
    //相当于先创建 int tmp = 1;    const int &ra = tmp;
}

引用用于函数的返回值

返回值的数据类型 & 函数名(形参列表);

****返回局部变量的引用,其本质是野指针

****可以返回函数的引用形、类的成员

int & func(int &no)
{
    no = 100;
    cout<<"no的地址:"<<&no<<",no="<<no<<endl;
    return no;
}

int main()
{
    int bh = 10;
    int &receive = func(bh);
    cout<<"bh的地址:"<<&bh<<",bh="<<bh<<endl;
    cout<<"receive的地址"<<&receive<<",receive="<<receive<<endl;
}

函数传值、传地址、传引用

不需要在函数中修改实参

****实参为数组时,用const指针

****实参是类时,使用const引用

需要修改实参

实参是数据内置类型,则使用指针,因为看到函数使用func(&x),就表示函数会修改x

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值