C++ Primer 笔记——函数

1.函数内的局部静态对象在程序的执行路径第一次经过对象定义语句的时候初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

size_t get_count()
{
    static size_t count = 0;
    return ++count;
}

2.当实参初始化形参的时候会忽略顶层const,换句话说,当形参有顶层const时,传给它常量或者非常量都是可以的。

const int ci = 1;    // ci的值不能被改变,const是顶层的
int i = ci;            // 当拷贝ci时, 忽略了它的顶层const

void fun(const int i){}
void fun(int i){}        // 错误,重复定义

3.数组在做形参的时候是不允许拷贝数组的,而且数组通常会被转换为指针。

// 以下三个声明是等价的,编译器只会检查参数是否为int*
void printArr(int *arr);
void printArr(int arr[]);
void printArr(int arr[10]);

4.如果想固定数组的大小,可以使用引用形参。

void printArr(int(&arr)[10]) {}
int arr[1] = { 0 };
printArr(arr);        // 错误,arr不是长度为10的数组

5.如果函数的实参数量未知,但类型都相同,可以使用 initializer_list 类型的形参,initializer_list是一种标准库类型,可以表示某种特定类型的值的数组。它和 vector 一样都是模板类型,但是不可以修改它的元素。

initializer_list<int> lt = { 1, 2, 3 };

6.省略符形参应该仅仅用于C和C++通用的类型,大多数类类型的对象在传递给省略符形参的时候无法正确的被拷贝。而且省略符形参只能出现在形参列表的最后一个位置。

void fun(...) {}
void fun1(int, ...) {}        // 其中的逗号可以省略
fun1(1, "test", "test2");

7.如果函数的返回类型不是void那么必须返回一个值,但是main函数可以例外,允许main函数没有return也可以正常结束,编译器将隐式的为main函数加入一个返回0的return语句。

8.main函数不可以递归,也不可以重载。

9.因为数组不能拷贝,所以函数不能返回数组,不过函数可以返回数组的指针或引用。

// 使用别名的方式
typedef int arrT[10];
using arrT = int[10];
arrT* func(int i);
// 不使用别名的方式
int (*func(int i))[10];
// 尾置返回类型的方式(C++ 11)
auto func(int i) -> int(*)[10];
// decltype 的方式(C++ 11)
int arr[10];
decltype(arr) *func(int i);

10.如果在函数内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。即在不同的作用域中无法重载函数名。

std::string read();
void print(const string&);
void print(double);

void Test()
{
    bool read = false;            // 新作用域,隐藏了外层的read
    std::string s = read();        // 错误,read是个bool值
    void print(int);            // 新作用域,隐藏了外层的print
    print("test");                // 错误,print(const string&)已被隐藏
    print(1);                    // 正确
    print(3.14);                // 正确,调用的是print(int)
}

11.constexper函数是指能用于常量表达式的函数,要遵循两个规定,函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句

constexpr int count() { return 1; }
constexpr int i = count();

12.允许constexpr函数的返回值并非一个常量

constexpr int count() { return 1; }
constexpr int total_count(std::size_t num) { return num * count(); };

但是上述的参数num如果给的不是一个常量,则编译器会发出错误信息。

13.和其他函数不一样,内联函数和constexpr函数可以在程序中多次定义,但是它的多个定义必须完全一致,基于这个原因,内联函数和constexpr函数通常定义在头文件中。

14.assert的行为依赖于一个名为NDEBUG的预处理变量的状态,如果定义了NDEBUG,则assert什么也不做。

15.当编译器无法找出最佳匹配的函数时会产生二义性的错误,我们可以使用强制类型转换来避免,但这不是一个好的选择。

void pritf(int, int);
void pritf(double, double);

pritf(1, 3.14);    // 编译器不知道该匹配哪个函数,产生二义性错误

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

int get_max(int a, int b);
int (*pf)(int, int);        // pf指向一个函数,该函数返回值是int(如果*pf不加括号,函数的返回值就是int*)

// 以下两个初始化等价
pf = get_max;
pf = &get_max;

//以下两个调用等价
int i = pf(1, 2);
int j = (*pf)(1, 2);

17.和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。

// 以下两个声明等价
void use_max(int a, int b, int pf(int,int));
void use_max(int a, int b, int (*pf)(int, int));

// 我们可以直接把函数作为实参使用,此时它会自定转换成指针
use_max(1, 2, get_max);

18.和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。

int get_max(int a, int b);

// 使用别名
using F = int(int, int);
using pF = int(*)(int, int);

pF f1(int);        // 正确
F f2(int);        // 错误,返回类型不会自动地转换成指针
F *f3(int);        // 正确

// 当然我们也能不用别名的形式直接声明
int (*f4(int))(int, int);

// 使用auto或者decltype
auto f5(int) -> int(*)(int, int);
decltype(get_max) *f6(int, int);
时间: 2024-10-05 10:31:25

C++ Primer 笔记——函数的相关文章

0715-----C++Primer听课笔记-----------函数指针 、单例模式

1.函数指针 1.1 普通成员函数指针包含类名信息以及const属性,指向具体函数是必须加上&符号. #include <iostream> using namespace std; class Test{ public: void setValue(const string &s, int a){ s_ = s; a_ = a; } void print() const{ cout << s_ << endl << a_ <<

C++ Primer笔记8_动态内存_智能指针

1.动态内存 C++中,动态内存管理是通过一对运算符完成的:new和delete.C语言中通过malloc与free函数来实现先动态内存的分配与释放.C++中new与delete的实现其实会调用malloc与free. new分配: 分配变量空间: int *a = new int; // 不初始化 int *b = new int(10); //初始化为10 string *str = new string(10, ); 分配数组空间: int *arr = new int[10];//分配的

C++ Primer笔记12_运算符重载_递增递减运算符_成员访问运算符

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.但是因为他们改变的正好是所操作对象的状态,所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本,因此,我们应该为类定义两个版本的递增与递减运算符. 问题来了,程序是如何区分前置和后置呢?因为都是++和-- 为了解决这个问题,后置版本的递增递减运算符接受一个额外的(不被使用)int类型的形参.当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参.这个形参唯一的作用就是区分前置和后置运算符函数. 因为不会

C++ Primer笔记2_四种类型转换_异常机制

1.类型转换 命名的强制类型转换: 有static_cast.dynamic_cast.const_cast.reinterpret_cast static_cast: 编译器隐式执行的任何类型转换都可以由static_cast完成 当一个较大的算术类型赋值给较小的类型时,可以用static_cast进行强制转换. 可以将void*指针转换为某一类型的指针 可以将基类指针强制转换为派生类指针,但是不安全. 无法将const转化为nonconst,这个只有const_cast才可以办得到 举例:

重读C++ Primer笔记

C++ Primer 5E 有符号和无符号 无符号类型和有符号类型混合运算时,有符号数会被提升至无符号类型,如果其值为负责会产生错误. int main() { unsigned int u = 10; int i = -42; std::cout<<u+i<< std::endl; // 4294967264 if sizeof(int)==4 return 0; } 列表初始化 列表初始化过程不允许损失数据类型精度,所以下面代码中的两行无法通过编译 int main() { d

C++ Primer笔记6_STL之泛型算法

1.泛型算法: 大多数算法定义在头文件algorithm中,标准库还在头文件numeric中定义了一组数值泛型算法 只读算法: 举例: find函数用于找出容器中一个特定的值,有三个参数 int val = 10;//val为我们需要查找的值 auto result = find(vec.begin(), vec.end(), val): cout << "The value "<< val << (result == vec.end() ? &qu

C++ Primer笔记13_运算符重载_总结

总结: 1.不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid 2.重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则如下: 运算符 建议使用 所有一元运算符 成员函数 = () [] -> 必须是成员函数 += -= /= *= ^= &= != %= >>= <<= , 似乎带等号的都在这里了. 成员函数 所有其它二元运算符, 例如: –,+,*,/ 友元函数 3.前几篇中的实例,现在汇总Person类的程序:

C++ Primer笔记4_类的静态成员_IO库

1.类的静态成员 static成员变量与函数 static成员变量:必须在类外初始化:(const或引用类型变量必须在构造函数初始化列表里初始化) static成员函数: 不依赖于类,相当于类里的全局函数(可以由该类对象调用,也可以 类名::函数名()的形式调用) 不包含this指针,不能声明为const,声明为const表示不会改变对象,而static成员函数存在于任何对象之外. 相当于把访问范围限制在所在的类中!  注意:不能访问类中非static成员变量以及非static成员函数. 注意:

C++ Primer笔记3_默认实参_类初探_名字查找与类的作用域

1.默认函数实参 在C++中,可以为参数指定默认值,C语言是不支持默认参数的,Java也不支持! 默认参数的语法与使用: (1)在函数声明或定义时,直接对参数赋值.这就是默认参数: (2)在函数调用时,省略部分或全部参数.这时可以用默认参数来代替. 注意事项: (1)函数默认值只能赋值一次,或者是在声明中,或者是在定义中,都可以. (2)默认参数定义的顺序为自右到左.即如果一个参数设定了缺省值时,其右边的参数都要有缺省值.比如int f(int a, int b=1,int c=2,int d=