C++Primer学习笔记 ----持续更新中

decltype

int fun()
{
    return 5;
}
i = 1


decltype(i) da; //da是int型
decltype((i)) db = i; //db是int&型,必须初始化
decltype(fun()) dc; //dc是int型,因为fun()返回值为int型
decltype(5) dd; // dd是int型,因为5是int型
decltype((5)) de; // de是int型,因为(5)是int型```
注意:只有对变量双括号时才是引用,其他的双括号均为原来的类型。

类型别名

  • typedef
  • using
  • #define
#define myInt2 int
typedef int myInt3;
int main()
{
       using myInt = int;
       myInt a = 1;
       myInt2 b = 2;
       myInt3 c = 3;
       cout << a << b << c << endl;
       return 0;
}
# 输出为 123

using 命名空间

注意:头文件中不应该包含using声明

标准库类型string

1. string在标准命名空间std里,所以代码开头要加入下述代码。
#include<string>
using std::string
2. 初始化方式
  1. 直接初始化
  2. 拷贝初始化
string s1("Hello")    //s1被直接初始化为"Hello"
string s2(n, "c")      //将s2直接初始化为n个“c”的字符串

string s3 = "Hi"     //拷贝初始化s3位“Hi”
3. 操作
  • os<<s
    将s写入到输出流
  • is>>s
    将输入流is中读取到的字符写入s,以空白字符分割
  • getline(is, s)
    从输入流is中读取一行,写入s
  • s.empty()
    s为空,返回true,否则返回false
  • s[n]
    下标索引,n=[0,–]
  • s.size()
    输出s的长度,也即字符个数。
    其实,s.size()返回的类型为string::size_type,最好使用unsigned int,因为vs2019是这么处理的。
4. cctype头文件中的函数
  • isalnum(c)
    当c是字母或数字时为真
  • isalpha(c)
    当c是字母时为真
  • iscntrl(c)
    当c是控制字符时为真
  • isdigit(c)
    当c是数字时为真
  • isgraph(c)
    当c不是空格但可打印时为真
  • islower(c)
    当c是小写字母时为真
  • isprint(c)
    当c是可打印字符时为真(即c是空格或c具有可视形式)
  • ispunct(c)
    当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种)
  • isspace(c)
    当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种)
  • isupper(c)
    当c是大写字母时为真
  • isxdigit(c)
    当c是十六进制数字时为真
  • tolower(c)
    如果c是大写字母,输出对应的小写字母;否则原样输出c
  • toupper(c)
    如果c是小写字母,输出对应的大写字母;否则原样输出c
注意,字符串加法时,必须保证至少含有一个字符串变量,两个字面值字符串是无法进行加法的。

vector

1. string在标准命名空间std里,所以代码开头要加入下述代码。
#include<vector>
using std::vector
2. 初始化
  • vector v1 = (10, 1)
    v1里面有十个1
  • vector v2 = {10, 1}
    v2里面有两个元素,10和1
  • vector v3 = {5, “Hi”}
    注意,虽然v3是大括号,但是因为5不是string类型,所以v3会被初始化为5个“Hi”
3. 操作
  • v.push_back(i)
    把i存入v的尾部
4. Tips

1. 因为vector会自动扩展大小,所以,除非是里面的元素完全一样,否则不用创建时设定大小
2. vector对象的类型总是包含着元素的类型
vector<int>: :size_type //正确
vector::size_type //错误

迭代器

字符串string是一个支持迭代器的容器,vector也是一个支持迭代器的容器

  • auto it = name.begin()
    得到一个指向容器开头元素的迭代器,大概可以认为是指针
  • auto it++会让迭代器指向容器内下一个元素
  • (*it)则是对迭代器的解引用,获取到当前位置的元素
  • auto it = name.end()
    得到一个指向容器尾部元素再往后一个位置的迭代器,所以end()这个迭代器并没有什么实际意义,仅仅作为一个标记,表明我们处理完了容器内所有元素。
auto it = name.end(), it– 后会获得容器内最后一个元素的迭代器
  • auto it = name.cbegin()
    cbegin()会获得容器的常量迭代器,效果等同于string::const_iterator it
string s("Hello");
auto it1 = s.begin()
string::iterator it2 = s.begin();
任何改变容器大小的操作均会使迭代器失效!

数组

定义数组时必须指定数据的类型,不允许使用auto,数组的元素必须是对象,不存在引用的数组
数组不支持拷贝赋值,也即不支持a2=a1这种写法

下面是重点
int arr[10];
int (*parr)[10] = &arr;

显然,parr是个指针,指向的什么呢?指向的是个数组,数组有10个元素,数组是int型的,这个数组是arr。又因为左边是个指针,所以右边也得是,所以就是&arr,而不能是arr

int (&refarr)[10] = arr;

refarr是个引用,引用的是一个数组,数组有10个元素,每个元素都是int型的,这个数组是arr,又因为引用就是引用,所以右边是arr本身,不需要是指针。

int *p[10];

p是一个数组,含有十个元素,每个元素都是整型指针。

代码示例

int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int* p[10];
int i = 0;
for (auto& adr : p)
{
    adr = &arr[i];
    i++;
}

这段代码把arr这个数组的每个元素的地址都存到了p这个指针数组里面

使用数组初始化vector
int int_arr[] = {1,2,3,4,5}
vector<int> v(begin(int_arr), end(int_arr));

运算

C++11开始商一律向下取整。

m/(-n) = (-m)/n = -(m/n)

m%(-n) = m%n

(-m)%n = -(m%n)

分支语句

switch的case语句中,最好在最后一个case上加上break,这样如果以后要增加分支语句,也不用在前面补充break了,会更安全,防止程序员出错。

即使不准备在default标签下做任何工作,定义一个default标签也是有用的。其目的是告诉程序的读者,我们已经考虑到了默认的情况,只是目前什么都没做。

!分支内可以声明变量,但是不能直接初始化
char a;
cin >> a;
switch (a)
{
    case 'a':
        int ival;
        ival = 2;
        cout << a << endl;
        break;
    case 'b':
         ival = 1;
         cout << ival << endl;
         cout << a << endl;
         break;
     default:
         cout << "Default." << endl;
         break;
}
!但是,加上作用域大括号后就可以,注意看case ‘a’
       char a;
       cin >> a;
       switch (a)
       {
       case 'a':
       {
              int ival = 2;
              cout << ival << endl;
              cout << a << endl;
              break;
       }
       case 'b':
              cout << a << endl;
              break;
       default:
              cout << "Default." << endl;
              break;
       }

try语句块和异常处理

  • throw表达式
  • try语句块
  • 一套异常类

1. throw表达式

真实的程序中,应该把对象相加的代码和用户交互的代码分离开,比如,如果两本书的ISBN不一样,抛出异常。

#include<stdexcept>

int isbn1,isbn2;
cin >> isbn1 >> isbn2;
if (isbn1 != isbn2)
{
      throw std::runtime_error::runtime_error("isbn");
}
cout << isbn1 + isbn2 << endl;

函数

局部静态对象

将局部变量定义成static类型,局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁。

分离式编译

CC -c a.c
CC -c b.cc
CC a.o b.o -o main # 生成main.exe

参数传递
  1. 传值
  2. 指针
void fun1(int* a)
{
    (*a)++;
    a++;
}
int m = 5;
int* pm = &m;
fun1(&m);
fun1(pm);
//执行完毕后m会变成7
虽然fun1()中改变了a这个指针的值,但是指针的值,或者说地址,是值传递,所以a是个局部临时变量,a的改变不会改变pm的指针地址
  1. 传引用
void fun2(int& a)
{
    a++;
}

int m = 5;
int& rm = m;
fun2(m);
fun2(rm);
//执行完毕后m变成7
含有可变形参的函数

C++11提供了一种initializer_list的标准库类型

如果实参的类型均相等但是个数未知,可以使用initializer_list类型的实参,initializer_list 是一-种标准库类型,用于表示某种特定类型的值的数组(参见3.5节,第101页)。initializer_list类型定义在同名的头文件中,并且需要使用std,也即using std::initializer_list。

操作含义
initializer_list 1st默认初始化: T类型元素的空列表
initializer_list lst{a,b,c…}lst的元素数量和初始值一样多, lst的元素是对应初始值的副本:列表中的元素是const
lst2(lst)拷贝或赋值一个 initializer_ list 对象不会拷贝列表中的元素;拷贝后, 原始列表和副本共享元素
lst2 = lst同上
lst.size()列表中的元素数量
lst.begin()返回指向 lst 中首元素的指针
lst.end()返回指向 lst 中尾元素下一位置的指针

注意,initializer_list接收的参数为用大括号括起来的一堆数据!

void fun(initializer_list<string> lst)
{
       for (auto beg = lst.begin(); beg != lst.end(); beg++)
              cout << *beg << endl;
}
int main(int argc, char* argv[])
{
       string s = "sss";
       fun({ "hello","aaa",s });
       return 0;
}

输出为:
hello
aaa
sss
不要返回局部对象的引用或指针

返回局部对象的引用是错误的;同样,返回局部对象的指针也是错误的,一旦函数完成,局部对象被释放,指针将指向一个不存在的对象。

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

Type (*function (parameter_list)) [dimension]

int (*func(int i)) [10]

func(int i) 意为调用func函数时需要一个int类型的实参
(* func(int i)) 意为我们可以对函数调用的结果执行解引用操作
(*func(int i)) [10] 表示解引用操作会得到一个大小是10的数组
int (*func(int i)) [10]表示数组中的元素是int类型

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值