函数

1. 参数传递


1.1 含有可变形参的函数


1. initializer_list

  initializer_list 是一种标准库类型,用于表示某种特定类型的值的数组。它可以接受多个参数,向量vector初始化等场合会用到。

void print(std::initializer_list<int> vals) {
	cout << vals.size() << ": ";
	for (auto i = vals.begin(); i != vals.end(); i++)
		cout << *i << " ";
	cout << endl;
}

int main() {
	print({1, 2});
}

  第 9 行使用大括号括住多个实参向可变形参传递参数。

2. 省略符形参
3. 可变参数模板

  可变参数模板在《模板与泛型编程》章节。

2. 返回类型


  大多数类型都能作为函数返回值,比如void、指针和引用。函数的返回值不能是数组类型或函数类型,但可以是它们的指针或引用。

1. 返回数组的指针或引用

  返回值可以使用类型别名,也可以不用。返回的是数组指针和引用而不是首元素的指针和引用,因而使用sizeof得到的是数组的大小。

int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
// 1.使用类型别名返回数组的指针和引用。
typedef int arr[10];
// using arr = int[10]; //作用同上一行。
arr* pa1() { return &a; }
arr& ra1() { return a; }
// 2.不用类型别名返回数组的指针和引用。
int(*pa2())[10] { return &a; }
int(&ra2())[10] { return a; }

int main() {
	int(*py)[10] = pa1(); //pa2用法相同。
	int (&ry)[10] = ra1(); //ra2用法相同。
	cout << sizeof(*py)<<" "<< sizeof(ry) << endl; //数组大小。
	cout << ry[3] << " " << (*py)[3] << endl; //a[3]的值。
}

2. 返回函数的引用

  函数一般只有指针,下面程序中把*换成&效果是一样的。

void fun() { cout << "--function." << endl; }
// 1.使用类型别名返回函数的指针。
typedef void (*Pf)();
//using Pf = void(*)(); //作用同上一行。
Pf pf1() { return fun; }
// 2.不用类型别名返回函数的指针。
void (*pf2())() { return fun; }

int main() {
	pf1()();
	pf2()();
}

3. 不能返回局部对象的指针或引用

  局部非静态对象在离开代码块时析构,返回的指针或引用关联的对象也就不存在了。

struct Example {
	int a = 7;
	~Example() { a = -7; }
};
Example f1() { Example e; return e; }
Example& f2() { Example e; return e; }
Example* f3() { Example e; return &e; }

int main() {
	Example e1 = f1(); cout << e1.a << endl; // 7
	Example e2 = f2(); cout << e2.a << endl; // -7
	Example& e3 = f2(); cout << e3.a << endl; // -7
	Example* e4 = f3(); cout << e4->a << endl; // -7
}

  第11、12、13行接收返回的局部变量的指针或引用。指针或引用关联的对象已经析构了,虽然还可以访问到析构前对象的部分成员,但是这些数据已经无效了,只是还没被覆盖。
  局部静态对象存储在静态内存,在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止才销毁。所以,可以返回局部静态对象的指针和引用。

4. 函数返回的过程

struct Example {
	Example() { cout << "--无参构造函数。" << endl; }
	Example(const Example& e) { a = e.a; cout << "--拷贝构造函数。" << endl; }
	Example& operator=(const Example& e) { a = e.a; cout << "--拷贝复制函数。" << endl; return *this; }
	Example(Example&& e) { a = e.a; cout << "--移动构造函数。" << endl; }
	Example& operator=(Example&& e) { a = e.a; cout << "--移动复制函数。" << endl; return *this; }
	~Example() { a = -7; cout << "--析构函数。" << endl; }
	int a = 7;
};

Example f1() { Example e; return e; }
Example& f2() { Example e; return e; }
Example* f3() { Example e; return &e; }

int main() {
	Example e11 = f1();  // 无参构造e、用e(移动构造临时对象、用临时对象)移动构造e11。
	Example& e12 = f1(); // 无参构造e、用e移动构造临时对象,用e12引用临时对象。
	Example e13; e13 = f1(); // 无参构造e13、无参构造e、用e移动构造临时对象、析构e、用临时对象移动复制给e13、析构临时对象。
	
	Example e21 = f2(); // 无参构造e、析构e、用析构后的e拷贝构造e21。
	Example& e22 = f2(); // 无参构造e、析构e、用e22引用析构了的e。
	Example* e3 = f3(); // 无参构造e、析构e、用e3指向析构了的e。
}

返回局部对象
  当返回局部非静态对象时,用返回值移动构造一个临时对象,再用这个临时对象:移动构造e11、关联e12(临时对象不析构)、移动复制给e13。其中移动构造e11这种情况会被优化成直接使用返回值移动构造e11。
  当返回局部静态对象时,用返回值拷贝构造一个临时对象,再用这个临时对象:XX构造e11、关联e12(临时对象不析构)、移动复制给e13。其中XX构造e11这种情况会被优化成直接使用返回值拷贝构造e11。
返回局部对象的指针或引用
  当返回局部非静态对象的指针或引用时,先析构局部对象,再用析构后的对象:拷贝构造e21、关联e22、关联e3。
  当返回局部静态对象的指针或引用时,直接使用返回值:拷贝构造e21、关联e22、关联e3。

  局部对象离开作用域会被析构,被指针或引用关联的临时对象不会被析构。
  当返回值类型不是指针也不是引用时,因为返回值是右值,用返回值赋值时优先调用移动函数,没有移动函数会调用拷贝函数,两者都没有会出错。比如智能指针unique_ptr的拷贝函数是删除的,它作为返回值类型时调用的就是移动函数。

3. 几种函数


1. 常量表达式函数

  一些地方必须使用常量表达式。比如:当一个模板实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替,这些值必须是常量表达式。
  常量、常量表达式、常量表达式函数都可以为一个常量表达式赋值。所以这里介绍一下常量表达式函数。
  常量表达式函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。

constexpr int get(int x) { return x + 1; }
int main() {
	const int a = 1;
	constexpr int b = 1;
	constexpr int y = get(a);
}

  把非constexpr函数定义成constexpr函数,编译时不报错,运行时会报错。

2. 内存操作函数

memcpy, memmove, memcmp, memchr, memset

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值