C++ primer 5学习第三天

自动对象

只存在于块执行器件的对象称为自动对象,当块的执行结束后,块中创建的自动对象的值变成未定义

局部静态对象

在经过定义语句时候对静态对象进行初始化,直到程序结束后对象才真正销毁

静态对象的默认初始值为0

函数声明

最好是将一些变量和函数的声明整合到一起,单独放在一个头文件内,方便后续管理

添加头文件

在vscode上进行配置后
我这里头文件都是用Mingw进行安装的

在调用这些自带的头文件要用括号括起<>

如果是自己写的头文件,则放到同一个目录下,使用双引号括起!!!
下面是CHAPTER6.h的内容

static int a;

接着是主程序

# include"CHAPTER6.h"
# include<iostream>
using namespace std;
int main(){

    cout<<a<<endl;
}

参数传递

参数传递主要分为传值传递引用传递

传值传递就是将实际值传递给形参,通过形参进行计算

而引用传递则是绑定了一个引用在已有的变量上,然后在其本身上进行计算

引用传递所带来的好处就是,避免了大型对象操作时先进行拷贝,再进行计算的过程。能节省大量运算时间

函数返回多个值

第一个方法就是建立一个新的数据类型,让其包含多个类型值

第二个方法就是在函数定义时,增加一个引用

例子:统计一个字符串数值,并且对原始字符串与别的字符串进行拼接

# include<iostream>
# include<string>
using namespace std;
string count(string s, int& crt){
    crt = s.size();
    return (s+"end!");
}

int main(){
    string s("ZZK!");
    int crt = 0;
    string s2 = count(s, crt);
    cout<<s2<<endl;
    cout<<crt<<endl;
}

这里我们的count就返回的时字符串拼接后的结果,而绑定了一个int引用crt,来对字符串内的字符进行统计

const形参和实参

第二章谈论到,顶层const就是其对象本身是一个常量

而在参数传递中,形参的顶层const被忽略掉了,也就是当形参用顶层const时候,传给它常量对象或者非常量对象都是可以的

尽量使用常量引用

首先常量引用能保护实参的值不被修改

其次常量引用能让函数接受更多类型的参数,而不会产生错误
比如我们上面写的count函数,它使用的是string对象

因此只能调用string对象

但如果我们想要用一个字符串字面值来传递参数

那我们需要把引用改成常量引用

string count(const string &s, int& crt){
    crt = s.size();
    return (s+"end!");
}

string s2 = count("sdasdsada", crt);

这样进行字面值参数传递就不会报错

数组形参

C++中不允许我们拷贝数组,因此如果传递数组到参数的时候,实质上是传递指向数组首元素的指针

通常数组管理有以下几种方法

通过标记指示数组

比如字符数组中,结束标记为’\0’,我们对数组进行操作的时候,每次都校验指针内容是否为空,若不为空再进行后续操作

使用标准库begin 和end

标准库begin 和 end 为我们提供了数组首尾的指针,我们可以根据这一特性对数组进行操作

显式指定数组大小

我们可以在参数传递同时,传递一个size_t 类型的参数,表示数组的大小

另外,不需要对数组进行写操作的时候,我们可以将数组定义为一个指向const的指针

即const int *p[]

数组引用形参

我们可以传递数组的引用作为形参,前提是在两边加上括号

void print(int (&arr)[10]){
    for(auto elem:arr){
        cout<<elem<<endl;
    }
}

一定要在&arr两边加上括号,这才表示的是数组的引用

如果是&arr[10] 则表示的是引用的数组

传递多维数组

多维数组就是数组的数组

因此我们传递的时候可以用一个指针代表首元素,这个元素它又指向的是一个含有多个数值的数组

void print(int(*matrix)[10], int rowSize)

等价于
void print(int matrix[][10])

处理命令行

int main(int argc, char **argv)

第一个形参指定的是形参数量,第二个形参传递的是指向字符串的指针

含有可变形参的函数

c++11提供了两种主要的方法

1.如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型

2.如果实参类型不同,可以编写一个特殊的函数,就是可变参数模板

initializer_list

它跟vector一样,也是模板类型,而且操作也基本相同

begin 和end方法都是返回首尾元素的指针

size返回整个列表的大小

我们在使用它的时候需要指定实参类型

initializer_list<string> ls;
void error_msg(initializer_list<string> s1){
    for(auto beg=s1.begin();beg!=s1.end();beg++){
        cout<<*beg<<endl;
    }
}
string i1("akakd"), i2("sadasd"), i3("dihiq");
error_msg({i1, i2, i3});

传递参数进行调用的时候,一定一定要用花括号框起来!!!

省略符形参

这是为了便于C++访问某些c程序中所设定的

但在大部分情况下,并不使用编程,故不介绍了

返回值

含有return语句的循环后面,应该也有一条return语句用于处理额外的情况

不要返回局部对象的引用和指针

函数是一种局部域

函数执行完后,局部对象也就随之消失,因此对应的内存空间就会被释放,如果你返回的是一个局部对象的引用,那么这个引用绑定的内存空间其实是会被释放的,因此是不安全的。

引用返回左值

引用返回的是左值,因此我们可以对此进行赋值,对引用进行修改

如果函数返回的是常量,那么我们直接赋值是会报错的

char &get_val(string &s, int ix){
    return s[ix];
}


string s("iloveu");
get_val(s, 3) = 'T';
cout<<s;

输出结果:
iloTeu

在函数那里添加一个&表示这个函数返回的是引用

列表初始化返回值

函数可以返回一个花括号包围的值的列表

vector<string> expect(vector<string> s1){
    if(s1.empty()){
        return {};
    }
    else{
        return {"easy", "dude"};
    }
}

vector <string> s1{"sada", "sdadas"};
vector <string> s2 = expect(s1);
for(auto i:s2){
    cout<<i<<endl;
}

返回函数自身

函数调用自身我们常常称为递归

比如我们写一个阶乘的函数

int fac(int val){
    if (val>1){
        return fac(val-1)*val;
    }
    else{
        return 1;
    }
}

返回数组指针

第一种方法就是使用类型别名

typedef int arrT[10];
using arrT = int[10];
arrT* func(int i); // 定义一个返回10个元素数组的指针的函数
int odd[] = {1, 3, 5, 7 ,9};
int even[] = {2, 4, 6, 8, 10};

arrT* array(int i){
    return(i%2)? &odd : &even;
}

int i = 5;
auto a = array(i);
    
for(auto c:*a){
    cout<<c;
}

第二种是直接声明

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

首先func(int i )表示这个函数接受一个int类型的参数
然后外层括号有一个解引用符号,说明func(int i) 是一个指针
最后有个[10] 说明这个指针指向的是含有十个元素的数组

int (*array(int i))[5]{
    return(i%2)? &odd : &even;
}

第三种是使用尾置返回类型

auto func(int i)-> int(*)[10];

我们把函数返回类型放到了形参列表后面
表示返回一个指针,指向含10个元素的数组

示例

auto array(int i) -> int(*)[5]{
    return (i%2)? &odd:&even;
}

第四种就是当我们知道要返回的数组的类型,就可以用decltype来声明返回类型

decltype(odd) *array(int i){
    return (i%2)?&odd : &even;
}

函数重载

同名函数但参数列表不同构成重载

要注意
前面说过顶层const不影响传入对象
因此下面的两个函数是构成不了重载函数

A a1(phone);
A a1(const phone);

这两个函数的参数表是一样的,因此不构成重载

const_cast和重载

const string &shorterString(const string &s1, const string &s2){
    return s1.size()<=s2.size()? s1:s2;
}

这是一段比较两个字符串长短的函数,函数将返回最短的字符串的引用

但是当我们传入的是两个非常量的string实参时候,它返回的结果依然是const string的引用

我们需要传入两个非常量的时候,返回普通引用

string &shorterString(string &s1, string &s2){
    auto &r = shorterString(const_cast<const string&>(s1), 
                            const_cast<const_string&>(s2));
    return const_cast<string &>(r);
}

首先将它的实参强制转换成对const的引用,然后调用了shorterString的const版本。再转回一个普通的string引用

内联函数

关键字inline,实质上是用空间换取时间,在编译的时候,将代码段替换到调用该函数的地方。前提是该函数不能有复杂的循环,而且用的次数较多。它对编译器仅仅是一个建议,当编译器认为函数过于复杂,也不会让其内联

constexpr函数

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

函数的返回值类型以及所有形参的类型都得是字面值类型
函数返回的不一定是常量类型

注意的是,内联函数和constexpr函数通常都放在头文件中,因为要保证其定义要一致

assert预处理宏

assert(expr)

对expr求值,如果表达式为假,则输出信息并终止程序

NDEBUG

在程序开头定义NDEBUG,则遇到了assert也不会退出程序

# define NDEBUG

一定是在程序第一行进行定义
它会让assert失效

函数指针

函数指针指向的是一个函数
我们使用函数名的时候,它会自动转换成一个指针

void test_func_pointer(){
    cout<<"AMD yes!"<<endl;
}

auto pf = &test_func_pointer;
auto pf2 = test_func_pointer;
pf();
pf2();

加不加取地址符都是一样的
(也可以把auto改成void)

重载函数的函数指针

在重载函数中,我们必须指定其对应使用的函数的参数类型

void ff(int *);
void ff(unsigned int);

void(*pf1)(unsigned int) = ff;

需要明确指出,这里ff指向的是unsigned类型函数

函数指针形参

我们传递形参的时候,不能定义函数类型的形参,但可以以函数指针的形式传入

void Bigger(const int a, bool pf(const int a1, const int a2))

这里传入的pf是一个函数指针
我们也可以使用别名来简化函数指针书写

返回指向函数的指针

using F = int(int *, int);// F是一个函数类型!
using PF = int (*)(int *, int); // PF是一个指针类型;

我们也可以这么写

int (*f1(int))(int *, int)

f1有形参列表,因此f1是个函数
f1前面有个*,说明返回指针
指针内有个int,说明返回的是int类型

也可以使用尾置方式

auto f1(int) -> int(*)(int *, int);

使用auto和decltype来指定返回类型

当我们明确知道使用的是哪个函数的时候也可以使用decltype来确定返回类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值