C++ primer 笔记

C++11引入了许多新特性,如nullptr用于空指针,constexpr允许编译时计算,using提供类型别名,auto简化变量声明,decltype获取表达式类型,范围for循环遍历容器,列表初始化创建对象,以及const和引用的更细致使用。此外,还包括对迭代器、字符串处理、多维数组、左值和右值、位运算符等方面的变化和增强。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++11新特性

long long

nullptr

        一种特殊类型的字面值,可以被转换成任何其他的指针类型。

int *p = nullptr;//得到空指针最直接有用的办法就是使用nullptr来初始化指针

constexpr

        一般来说,如果你认定变量是一个常量表达式,那就把它声明称constexpr类型。允许将变量声明为constexpr类型以便编译器来验证变量的值是否是一个常量表达式。声明为const的变量一定是一个常量,而且必须用常量表达式初始化。

constexpr int sz = size(); //只有当size是一个constexpr函数时,才是一条正确的声明语句

        一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。

const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr; //q是一个指向整数的常量指针
//p和q相差甚远,关键在于constexpr把它所定义的对象置为顶层const

 using

        使用别名声明来定义类型的别名

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

auto

        auto能让编译器替我们去分析表达式所属的类型。通过初始值来推算变量的类型。

        auto必须有初始值       

//auto一般会忽略顶层const,同时保留底层const。
//若希望推断出的auto类型是一个顶层const,需明确指出:
const auto f = ci; //ci推演出来的是int,f是const int
//也可以将引用的类型设为auto,此时原来的初始化规则仍适用:
auto &g = ci;
auto &h = 42; //error:不能将非常量引用绑定字面值
const auto &j = 42; //right:可以将常量引用绑定字面值

decltype

        作用:选择并返回操作数的数据类型。(类型说明符)

//编译器分析表达式并得到它的类型,却不实际计算表达式的值
decltype(f()) sum = x; //sum的类型就是函数f返回的类型
                      //编译器并不实际调用f,而是使用当调用发生时f的返回类型作为sum的类型
//如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x 的类型是const int
decltype(cj) y = 0; //y 的类型是const int&, y必须绑定到变量x
decltype(cj) z; //error: z是一个引用,必须初始化

        decltype的结果类型与表达式形式密切相关

//decltype的表达式如果是加上括号的变量,结果必将是引用
decltype((i)) d; //error, d是一个int&,必须初始化
decltype(i) e; //right, e是一个未初始化的变量

        如果我们知道函数返回的指针将指向哪个数组,就可以使用关键字decltype声明返回类型。但decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrptr返回指针还必须在函数声明时加一个*符号 

 类内初始值in-class initializer

        初始化为0、空字符串...

范围for(range for)

        遍历给定序列中的每个元素并对序列中的每个值执行某种操作

//语法形式
for ( declaration : expression )
    statement
//declaration -- 负责定义一个变量,用于访问序列中的基础元素
//expression -- 一个对象
//每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素

//e.g. 把string对象中的字符每行一个输出出来
string str("some string");
for (auto c : str)
    cout << c <<endl;
//每次迭代,str的下一个字符被拷贝给c

        如果使用for改变字符串中的字符,则需要使用引用改变它绑定的字符

列表初始化

        用花括号括起来的0个或多个初始元素值被赋给vector对象

vector<string> v{"a","an","the"};

cbegin和cend

        类似于begin和end,也将返回指示容器的第一个元素或者最后一个元素下一个位置的迭代器。但是有所不同的是不论vector对象和string对象本身是否为常量返回值都是const_iterator

difference_type

        两个指向同一个容器中的元素或者尾元素的下一个位置的迭代器相减,所得结果是两个迭代器的距离,类型是名为diffence_type的带符号整数型string和vector都定义了difference_type,距离可正可负

非成员函数的begin和end函数

        将数组作为它们的参数/* begin(arr) || end(arr) */ ,返回指向arr首元素的指针或指向arr尾元素下一个位置的指针

        这两个函数都定义在iterator头文件中

除法的舍入规则

        一律直接向0取整(即直接切除小数部分)

将sizeof用于类成员

        允许使用作用域运算符来获取类成员的大小

sizeof Sales_data::revenue; 
//获取Sales_data的revenue大小的方式

initializer_list形参

        一种标准库类型,用于表示某种特定类型的值的数组。定义在同名头文件中

        如果函数的实参数量未知但是全部实参的类型都相同,可以使用initializer_list类型的形参

        类似vector,initializer_list也是一种模版类型,定义initializer_list对象时,必须说明表中所含元素的类型;和vector不一样的是,initializer_list对象中的元素永远都是常量值,我们无法改变initializer_list对象中元素的值


笔记

viod* 指针

        一个void* 指针存放着一个地址,但是对该地址中到底是什么类型的对象并不清楚

double obj = 3.14, *pd = &obj;
void *pv = &obj; //obj可以是任意类型的对象
pv = pd; //right:pv可以存放任意类型指针

        指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*

extern关键字

        默认情况下,const对象被设定在仅在文件内有效。如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。

//file_1.cc定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize = func();
//file_1.h头文件
extern const int bufSize; //与file_1.cc中定义的bufSize是同一个

顶层const和底层const

int i = 0;
int *const p1 = &i; //不能改变p1的值,顶层const
const int ci = 42; //不能改变ci的值,顶层const
const int *p2 = &ci; //允许改变p2的值,底层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的const是底层const
const int &r = ci; //用于声明引用的const都是底层const

        当执行对象的拷贝操作时,常量是顶层const不受什么影响;底层const的限制却不能忽视。执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量。

getline

        getline(cin, str); 如果想在最终字符串中保留空格符,则应用getline函数代替原先的>>运算符:当遇到换行符为止(换行符也被读入,但不存入)。getline返回的是流参数。

标准库类型--string

        str.empty()函数根据string对象是否为空返回对应bool值

        str.size()函数返回string对象的长度(即string对象中字符的个数)

        所有用于存放string类的size函数返回值的变量,都应该是string::size_type(一个无符号类型的值,且能足够存放下任何string对象的大小)类型的

        当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string

        想要访问string对象中的单个字符可以使用下标运算符( s[0] ~ s[s.size()-1] )(接受的输入参数是string::size_type类型的值,参数表示要访问的字符法位置,返回值是该位置上的字符的引用)或者使用迭代器

cctype头文件

        定义了一组标准库函数去了解某个字符的特性

        c语言中的name.h在C++中会被命名为cname,表示属于C语言标准库的头文件,在C++中属于命名空间std

编译器根据模版创建类或函数的过程称为实例化

标准库类型--vector

        对象的集合,集合中的每个对象都有一个与之对应的索引,索引用于访问对象。vector“容纳”着其他对象,所以也常被称为“容器”

        push_back() -- 向vector对象中添加元素

        vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。对于下标只能对确知已存在的元素执行下标操作(确保下标合法的有效方法是尽可能使用范围for语句)

迭代器iterator

        所有标准库容器都可以使用迭代器,只有少数几种才同时支持下标运算符

        有迭代器的类型同时返回迭代器成员:如,end成员返回指向容器“尾元素的下一个位置(on past the end)”的迭代器,即该迭代器没有什么实际含义,,只是一个标记。end成员返回的迭代器常被称为尾后迭代器(off-the-end iterator)或简称为尾迭代器(end iterator)

        如果vector对象或string是一个常量,则需使用const_iterator;

vector<int>::iterator it;
string iterator it2;

vector<int>::const_iterator it3;
string const_iterator it4;

         迭代器的三种不同含义:一,迭代器概念本身;二,容器定义的迭代器类型;三,某个迭代器对象

        begin和end返回的具体类型由对象是否为常量决定。若对象为常量,返回const_iterator;若对象不是常量,则返回iterator

数组

        定义数组时必须指定数组类型,不允许使用auto关键字

        数组的元素为对象,不存在引用的数组

        从数组的名字开始按照由内向外的顺序阅读,比较好理解数组声明的含义

        使用数组下标时,通常定义成size_t类型 -- 一种机器相关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小(定义在cstddef头文件中)

        两个指针相减的结果的类型是一种名为ptrdiff_t的标准库类型,定义在cstddef头文件中,结果可正可负,所以ptrdiff_t是带符号类型

        数组特性:在很多用到数组名字的地方,编译器都会自动的将其替换为一个指向数组首元素的指针

        标准库类型限定使用的下标必须是无符号类型,而内置的下标运算并无此要求

        内置的下标运算所用的索引值不是无符号类型,这一点与vector和string不一样

C标准库string函数

        strlen(p);  strcmp(p1, p2);  strcat(p1, p2);  strcpy(p1, p2);

        传入此类函数的指针必须指向以空字符作为结束的数组

        当使用数组的时候其实真正使用的是指向数组的首元素的指针

        比较string对象的时候,用的是普通的关系运算符和相等性运算符;如果把这些运算符用在两个C风格字符串上,实际比较的是指针而非字符串本身,要想比较两个C风格字符串需要调用strcmp函数,此时就不再是比较指针了

混用string对象和C风格字符串

        如果程序的某处需要一个C风格字符串,无法直接用string对象来代替它。如,不能用string对象直接初始化指向字符的指针。为了完成该功能,string专门提供了一个名为c_str的成员函数

string s("hello world");
char *str = s; //error,不能用string对象初始化char*
const char *str = s.c_str(); //right
//函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,所存数据恰好与string对象的一样
//结果指针类型为const char*,从而确保不会改变字符数组内容
//后续若改变s的值可能让之前返回的数组失去效用
//若执行完c_str()函数后程序想一直都能使用其返回的数组,最好将该数组重新拷贝一份

多维数组

        C++中通常说的多维数组其实是数组的数组

        使用范围for语句处理多维数组时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型

        通过使用auto或decltype就能尽可能避免在数组前加上一个指针类型了

        循环遍历多维数组时,也可以用标准库函数begin和end 

左值和右值

        当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值当时候,用的是对象的身份(在内存中的位置)。在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用

        使用关键字decltype时,左值和右值有所不同。如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型。//如p是int*类型,因为解引用运算符生成左值,所以decltype(*p)的结果是int&;因为去地址运算符生成右值,所以decltype(&p)的结果是int**(指向整型指针的指针)

求值顺序

        在大多数情况下不会明确规定何时以及如何对运算对象求值。有4种运算符明确规定了运算对象的求值顺序:逻辑与(&&)运算符--先求左侧运算对象的值,只有左侧值为真时才会继续求右侧对象的值;逻辑或(||)运算;条件(?:)运算符;逗号(,)运算符

        在多个函数进行运算时,对于这些函数的调用顺序没有明确规定。若它们是无关函数,它们既不会改变同一对象的状态也不执行IO任务,那么函数调用不受限制;反之,若其中某几个函数影响同一对象,则它是一条错误的表达式,将产生未定义的行为

位运算符

        提供检查和设置二进制位的功能

        左移运算符(<<)在右侧插入值为0的二进制位。右移运算符(>>)的行为则依赖左侧对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位;如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定

移位运算符 原理示例

         位求反运算符(~)将运算对象逐一求反后生成一个新值

位求反运算符 原理示例

         与(&)、或(|)、异或(^)

        移位运算符(I/O运算符)满足左结合律 

sizeof运算符

        满足右结合律,其所得值是一个size_t类型的常量表达式(所以可以用sizeof的结果声明数组的维度

//sizeof运算符的运算对象的两种形式
//sizeof (type)
Sales_data data, *p;
sizeof(Sales_data); //存储Sales_data类型的对象所占空间大小
//sizeof expr
sizeof p; //指针所占空间大小
sizeof *p //p所指类型的大小,即sizeof(Sales_data)
sizeof data //data类型的大小,即sizeof(Sales_data)

        sizeof运算符的结果部分地依赖于其作用类型:

                char/类型为char的表达式 -- 1

                引用 -- 被引用对象所占空间大小

                指针 -- 指针本身所占空间大小

                解引用 -- 指针指向的对象所占空间大小,指针不需要有效

                数组 -- 整个数组所占空间大小,sizeof运算不会把数组转化成指针来处理

                string对象/vector对象 -- 只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间

逗号运算符comma operator

        含有两个运算对象,从左向右的顺序依次求值。经常被用在for循环中。首先对左侧表达式求值,然后将求值结果丢弃。逗号运算符真正的结果是右侧表达式的值。如果右侧是左值,那么最终求值结果也是左值

命名的强制类型转换

        cast-name<type>(expression);

        // type--转换的目标类型( 如果type是引用类型,则结果是左值);expression--要转换的值;cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast中的一种,指定了执行的是哪种转换

        static_cast:任何具有明确定义的类型转换只要不包含底层const,都可以使用static_cast。当需要把较大的算术类型赋值给较小的类型时,使用static_cast将不会出现warning;static_cast对于编译器无法自助执行的类型转换也非常有用

        去掉const性质cast away the const -- 将常量对象转换成非常量对象的行为

        const_cast:只能改变对象的底层const,常用于函数重载

const char *pc;
char *p = const_cast<char*>(pc);//right,但是通过p写值是未定义的行为
//如果对象本身不是一个常量,则使用强制类型转换获得写权限是合法的行为
//若对象本身是一个常量,再使用const_cast执行写操作就会产生未定义的后果
char *q = static_cast<char*>(pc);//error, static_cast不能转换掉const性质
static_cast<string>(pc);//right,字符串字面值转换成string类型
const_cast<string>(pc);//error, const_cast只改变常量属性

        reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释 ]

语句

        最简单的语句是空语句,只含有一个单独的分号

        复合语句也被称为块,一个块就是一个作用域。空块--内部没有任何语句的花括号

        悬垂else -- C++规定else与离他最近的尚未匹配的if匹配,从而消除程序的二义性

        case标签(case关键字+它对应的值)-- 必须是整型常量表达式

        如果没有任何一个case标签能匹配上switch表达式的值,程序将执行紧跟在default标签后的语句 。若switch结构以一个空的default标签作为结束,则default标签后面必须跟上一条空语句或一个空块

        迭代语句 -- 循环

        goto语句 -- 一种跳转语句,从goto语句无条件跳转到同一函数的另一条语句

//不要在程序中使用goto语句,因为它会使程序既难理解又难修改
//语法形式
goto label;
//label -- 用于标识一条语句的标示符
//label statement -- 一种特殊语句,在它之前有一个标识符和一个冒号
end: return;//带标签语句,可以作为goto的目标

        标签标示符独立于变量或者其他标识符的名字,因此标签标示符可以和程序中其他实体的标示符使用同一个名字而不会互相干扰

异常处理  (throw,try语句块,标准异常)    

        异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持

        C++标准库定义了一组类,用于报告标准库函数遇到的问题。这些异常类也可以在用户编写的程序中使用,分别定义在4个头文件中:exception,stdexcept,new,type_info

        throw,try-catch -- 程序的异常检测部分使用throw表达式引发一个异常;异常处理部分使用try语句块处理异常,try语句块中代码抛出的异常通常会被某个catch子句处理,因为catch子句“处理”异常,所以它们也被称为异常处理代码exception handler

//练习 5.23 -- 编写一段程序,从标准库读入两个整数,输出第一个数除以第二个数的结果
#include<iostream>
using namespace std;

int main(){
    cout << "请依次输入被除数和除数: " << endl;
    int ival1, ival2;
    cin >> ival1 >> ival2;
    if(ival2 == 0){
        cout << "除数不能为0" <<endl;
        return -1;
    }
    cout << "两数相除的结果是: " << ival1 / ival2 << endl;
    return 0;
}

//练习 5.24 -- 修改程序,使当第二个数为0时抛出异常(先不设定catch语句)
#include<iostream>
#include<stdexcept>//++
using namespace std;

int main(){
    cout << "请依次输入被除数和除数: " << endl;
    int ival1, ival2;
    cin >> ival1 >> ival2;
    if(ival2 == 0){
        throw runtime_error("除数不能为0");//cout << "除数不能为0" <<endl;
        //return -1;
    }
    cout << "两数相除的结果是: " << ival1 / ival2 << endl;
    return 0;
}

//练习5.25 使用try语句块捕获异常
//catch子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行try语句块的内容
#include<iostream>
#include<stdexcept>
using namespace std;

int main(){
    cout << "请依次输入被除数和除数: " << endl;
    int ival1, ival2;
    while(cin >> ival1 >> ival2)//cin >> ival1 >> ival2;
    {
        try
        {
            if(ival2 == 0)
            {
                throw runtime_error("除数不能为0");
            }
            cout << "两数相除的结果是:" << ival1 / ival2 << endl;
        }catch(return_error err)
        {
            cout << err.what() << endl;
            cout << "需要继续吗(y or n)?";
            char ch;
            cin >> ch;
            if(ch != 'y' && ch != 'Y')
                break;
        }
    }
    /* -- 5.24
    if(ival2 == 0){
        throw runtime_error("除数不能为0");//cout << "除数不能为0" <<endl;
        //return -1;
    }
    cout << "两数相除的结果是: " << ival1 / ival2 << endl;
    */
    return 0;
}

函数基础

        局部变量local variable -- 形参和函数内部定义的变量。对函数来说是“局部”的,仅在函数的作用域内可见,同时还会隐藏在外层作用域中同名的其他所有声明中

        自动对象 -- 只存在于块执行期间的对象称为自动对象automatic object。当块的执行结束后,块中创建的自动对象的值就变成未定义的了

        局部静态对象local static object -- 令局部变量的生命周期贯穿函数调用及之后的时间,在程序执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响

使用引用避免拷贝

        拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本就不支持拷贝操作

        当函数无需修改引用形参的值时最好使用常量引用

形参与const

        顶层const作用于对象本身,当用实参初始化形参时会忽略掉顶层const(形参的顶层const被忽略掉了)

        可以使用非常量初始化一个底层const对象,但是反过来不行;同时一个普通的引用必须用同类型的对象初始化、

省略符形参

        使用了名为varargs的C标准库功能(仅用于C和C++通用的类型,大多数类类型的对象在传递给省略符对象时都无法正确拷贝

        省略符形参只能出现在形参列表最后一个位置

返回值

        不要返回局部对象的引用或指针:函数终止意味着局部变量的引用将指向不再有效的内存区域;局部对象被释放,指针将指向一个不存在的对象

        能为返回类型是非常量引用的函数的结果赋值;若返回类型是常量引用,不能给调用的结果赋值

        main函数的返回值可以看作状态指示器:返回0代表执行成功,返回其他值表示执行失败,其中非0值的具体含义依依机器而定 

//cstdlib头文件定义了两个预处理变量,可以用这两个变量表示成功与失败
int main(){
    if (some_failure)
        return EXIT_FAILURE;
    else
        return EXIT_SUCCESS;
    return 0;
}
//预处理变量,既不能在前面加上std::,也不能在using声明中出现

声明一个返回数组指针的函数

        type ( *function (parameter_list) ) [dimension];

        //type--元素的类型;dimension--数组的大小;( *function (parameter_list) )--两端括号必须存在

//e.g.
int (*func(int i))[10];
//1--func(int i)表示调用func函数时需要一个int类型的实参
//2--(*func(int i))表示可以对函数调用的结果执行解引用操作
//3--(*func(int i))[10]表示解引用func的调用将得到一个大小是10的数组
//4--int (*func(int i))[10]表示数组中的元素是int类型

函数重载

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值