栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 函数的可变形参,一些处理方法(initializer

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 函数的可变形参,一些处理方法(initializer

C++ 函数的可变形参
Part 1 - initializer_list

only C++
适用:参数类型相同,但参数个数不定
来历:C++ 标准库

使用它,需要包含头文件initializer_list,命名空间为std

俯瞰
#include 

int add(initializer_list nums)
{
	int result = 0;
    for (auto nitr = nums.begin(); nitr != nums.end(); nitr++) {
       result += (*nitr);
    }
    return result;
}

// 调用
int main() {
	// 注意:不要漏掉花括号
	int r = add({-3, 2, 5, 1});
	return 0;
}
端详
#include 
using namespace std;

initializer_list list;						// 默认初始化,空列表
initializer_list list{e1, e2, e3, ...};		// 另一种初始化方式
list2 = list1;									// 拷贝
list.begin();									// 列表首元素指针
list.end();										// 列表最后一个元素下一个位置的指针
list.size();									// 列表中的元素数量

initializer_list是一个模板类型
initializer_list是一个线性容器,我们姑且称之为“列表”
initializer_list需要接受一个类模板参数
e.g. initializer_list list;

取用initializer_list里面的元素的方式,类似于vector等STL容器
对于一个initializer_list对象:

调用begin()与end(),分别来获取列表的头和列表最后一个元素的下一个位置的指针,需要注意的是,和vector等STL容器不同,begin()或end()返回的是const T *型的裸指针调用size()来获取列表长度initializer_list对象之间的拷贝或赋值,只改变列表对象本身,而在内存中共享同一份存储的数据initializer_list对象中存储的数据都是拷贝过来的副本,且都为const型,无法更改

// 头文件、命名空间等代码已省略
int main()
{
	initializer_list lst;
	lst = {1, 3, 2, 4};
	const int * np = lst.begin();	// 返回的是原始对象的const型裸指针
	*np = 3;	// 错误: 列表里面的值都为const型,不能修改

	initializer_list lst2 = lst;
	cout<< (lst2.begin() == lst.begin()) << endl;	// 1,即true:赋值或拷贝时在内存中共享元素
	 
	return 0;
}

那么,试试把initializer_list与函数形参结合起来:

// 头文件、命名空间等代码已省略

int add(initializer_list nums)
{
	int result = 0;
	
	// 这里的 auto 也可以写成 const int *
    for (auto nitr = nums.begin(); nitr != nums.end(); nitr++) {
       result += (*nitr);
    }
    return result;
}

int main()
{
	cout << add({1, 2, 5}) << endl;		// 输出:8
	return 0;
}

Part 2 - stdarg/varargs 省略符形参

Both C and C++
适用:不定参数类型,不定参数个数
来历:C标准库

使用它,需要包含头文件stdarg.h

俯瞰
#include 
#include 

using std::cout;
using std::endl;

// n: 几个数字相加
// 调用时,我们在最后再传一个字符串进去,让它把字符串打印出来
int add(int n, ...)
{
	va_list lst;
	va_start(lst, n);
	int result = 0;
	
	for (int i = 0; i < n; i++) {
		result += va_arg(lst, int);
	}
	
	cout << va_arg(lst, const char *) << endl;
	
	va_end(lst);
	return result;
}

int main()
{
	int r = add(3, 1, 8, 2, "Hello World");		// r = 11
	cout << "The result is " << r << endl;
	
	return 0;
}
端详
#include 

typedef char * va_list;						// va_list 的底层定义
void va_start(va_list ap, last);			// 宏,初始化ap
type va_arg(va_list ap, type);				// 宏,取参数
void va_copy(va_list dest, va_list src);	// 宏,拷贝 va_list 对象
void va_end(va_list ap);					// 宏,结束时调用

va_...()都是宏va_list是一个类型,实质上是一个char *,表示所操作的函数实参列表,此类对象的作用类似于句柄,因此,每个va_...()宏的第一个参数都是va_list型对象一般的操作流程为:

    定义va_list型对象,假设其为lst.调用va_start()宏,第二个参数填函数的可变参数开始之前的一个形参名,这是为了定位到第一个可变参数开始的地址,但是,在现代编译器中,此参数可以填别的什么已定义的标识符,编译器会警告,但可以正常发挥作用调用va_arg()宏,第二个参数是一个类型名,表示你当前取的参数的类型,va_arg()会按顺序取参数,第一次调用时就取第一个参数,第二次调用时就取第二个参数,以此类推调用va_end()宏,结束对可变参数列表的使用

一份示例代码

#include 
#include 
#include 
#include 

using std::cout;
using std::endl;
using std::string;
using std::initializer_list;


// Part 1 - initializer_list
void say_case1(initializer_list strs)
{
    for (const string * itr = strs.begin(); itr != strs.end(); itr++) {
        cout << *itr << endl;
    }
}

// Part 2 - stdarg/varargs 省略符形参
// Param n: 实参数量
// 最后传进去两个数字,然后输出相加的结果
void sayAndAdd_case2(int n, ...)
{
    va_list lst;
    va_start(lst, n);
    for (int i = 0; i < n - 2; i++) {
        cout << va_arg(lst, const char *) << endl;
    }

    int a = va_arg(lst, int);
    int b = va_arg(lst, int);
    cout << a << " + " << b << " = " << a + b << endl;

    va_end(lst);
}

int main()
{
    say_case1({"Hello", "I am initializer_list."});
    sayAndAdd_case2(5, "Hi", "I am varargs.", "Nice to Meet youuu~", 3, 5);

	return 0;
}

附:
std::initializer_list
stdarg(3) — Linux manual page

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/743436.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号