c++函数参数安全
C++ function argument safety
在接受多个相同类型参数的函数中,如何保证调用者不会弄乱顺序?
例如void allocate_things(int num_buffers, int pages_per_buffer, int default_value ...
和
// uhmm.. lets see which was which uhh..
allocate_things(40,22,80,...
一个典型的解决方案是将参数放在一个结构中,并使用命名字段。
AllocateParams p;
p.num_buffers = 1;
p.pages_per_buffer = 10;
p.default_value = 93;
allocate_things(p);
当然,您不必使用字段。
如果您有c++ 11编译器,您可以将用户定义的字面值与用户定义的类型结合使用。下面是一个简单的方法:
struct num_buffers_t {
constexpr num_buffers_t(int n) : n(n) {} // constexpr constructor requires C++14
int n;
};
struct pages_per_buffer_t {
constexpr pages_per_buffer_t(int n) : n(n) {}
int n;
};
constexpr num_buffers_t operator"" _buffers(unsigned long long int n) {
return num_buffers_t(n);
}
constexpr pages_per_buffer_t operator"" _pages_per_buffer(unsigned long long int n) {
return pages_per_buffer_t(n);
}
void allocate_things(num_buffers_t num_buffers, pages_per_buffer_t pages_per_buffer) {
// do stuff...
}
template <typename S, typename T>
void allocate_things(S, T) = delete; // forbid calling with other types, eg. integer literals
int main() {
// now we see which is which ...
allocate_things(40_buffers, 22_pages_per_buffer);
// the following does not compile (see the 'deleted' function):
// allocate_things(40, 22);
// allocate_things(40, 22_pages_per_buffer);
// allocate_things(22_pages_per_buffer, 40_buffers);
}
目前为止有两个不错的答案,还有一个:另一种方法是尽可能地利用类型系统,并创建强类型定义。例如,使用boost strong typepedef (http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/strong_typedef.html)。
BOOST_STRONG_TYPEDEF(int , num_buffers);
BOOST_STRONG_TYPEDEF(int , num_pages);
void func(num_buffers b, num_pages p);
调用带参数顺序错误的func现在会导致编译错误。
关于这一点有几点注意。首先,boost的强类型定义方法相当过时;使用可变的CRTP可以做更好的事情,并且完全避免使用宏。其次,这显然会带来一些开销,因为您经常需要显式地进行转换。所以一般来说,你不想过度使用它。对于那些在你的图书馆里反复出现的东西来说,这真的很好。对于一次性出现的事情来说不太好。例如,如果你正在编写一个GPS库,你应该有一个强double类型的pedef来表示以米为单位的距离,一个强int64类型的pedef来表示以纳秒为单位的时间,等等。
(注:帖子最初标记为'C ')
C99以后允许扩展到@Dietrich Epp的想法:复合文字
struct things {
int num_buffers;
int pages_per_buffer;
int default_value
};
allocate_things(struct things);
// Use a compound literal
allocate_things((struct things){.default_value=80, .num_buffers=40, .pages_per_buffer=22});
甚至可以传递结构的地址。
allocate_things(struct things *);
// Use a compound literal
allocate_things(&((struct things){.default_value=80,.num_buffers=40,.pages_per_buffer=22}));
你不能。这就是为什么建议使用尽可能少的函数参数。
在您的示例中,您可以有单独的功能,如set_num_buffers(int num_buffers)
, set_pages_per_buffer(int pages_per_buffer)
等。
你可能已经注意到allocate_things
不是一个好名字,因为它没有表达函数实际在做什么。特别是我不希望它设置一个默认值
为了完整起见,当调用变成。时,可以使用命名参数。
void allocate_things(num_buffers=20, pages_per_buffer=40, default_value=20);
// or equivalently
void allocate_things(pages_per_buffer=40, default_value=20, num_buffers=20);
然而,在当前的c++中,这需要相当多的代码来实现(在头文件中声明allocate_things()
,它还必须声明适当的外部对象num_buffers
等,提供operator=
返回一个唯一的合适对象)。
----------工作示例(用于sergej)
#include <iostream>
struct a_t { int x=0; a_t(int i): x(i){} };
struct b_t { int x=0; b_t(int i): x(i){} };
struct c_t { int x=0; c_t(int i): x(i){} };
// implement using all possible permutations of the arguments.
// for many more argumentes better use a varidadic template.
void func(a_t a, b_t b, c_t c)
{ std::cout<<"a="<<a.x<<" b="<<b.x<<" c="<<c.x<<std::endl; }
inline void func(b_t b, c_t c, a_t a) { func(a,b,c); }
inline void func(c_t c, a_t a, b_t b) { func(a,b,c); }
inline void func(a_t a, c_t c, b_t b) { func(a,b,c); }
inline void func(c_t c, b_t b, a_t a) { func(a,b,c); }
inline void func(b_t b, a_t a, c_t c) { func(a,b,c); }
struct make_a { a_t operator=(int i) { return {i}; } } a;
struct make_b { b_t operator=(int i) { return {i}; } } b;
struct make_c { c_t operator=(int i) { return {i}; } } c;
int main()
{
func(b=2, c=10, a=42);
}
你真的要尝试QA任意整数的所有组合吗?然后把所有的负/零值检查都扔进去?
只需创建两个枚举类型,分别用于最小、中等和最大缓冲区数量,以及较小、中等和较大的缓冲区大小。然后让编译器完成工作,让你的QA人员休息一个下午:
allocate_things(MINIMUM_BUFFER_CONFIGURATION, LARGE_BUFFER_SIZE, 42);
那么你只需要测试有限数量的组合,你就会有100%的覆盖率。5年后为你的代码工作的人只需要知道他们想要实现什么,而不必猜测他们可能需要的数字或哪些值实际上已经在该领域进行了测试。
它确实使代码稍微难以扩展,但听起来这些参数是用于低级性能调优的,因此不应将摆弄这些值视为廉价/琐碎/不需要彻底测试。变更的代码审查Allocate_something (25,25,25);
…
allocate_something(30, 80, 42);
…可能只是耸耸肩/被甩掉,但是对一个新的枚举值EXTRA_LARGE_BUFFERS的代码审查可能会引发所有关于内存使用、文档、性能测试等的正确讨论。
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 为表示一个或多个操作的C++函数的int参数寻找类型安全的替换
- 类型安全可变参数函数
- 编译器在 const ref 类型参数上使用临时对象时是否应该警告不安全的行为?
- 提供带有常量参数的函数指针作为带有非常量参数的函数指针是否安全?
- C++:传递像 unique_ptr:get() 这样的参数来函数是否安全?
- 使用安全模式从DLL(通过(INTPTR)参数传递到该函数)获取char **
- C 11-当我将局部变量作为参数传递到线程中时,它是否安全
- 如何要求接口中参数的线程安全
- 为什么我可以通过这样的字符串参数?这种样式安全吗?
- 与构造函数参数相关的异常安全的习语
- 键入安全字符串参数
- 在传递的对象即将被销毁之前,发出将QObject指针作为参数传递的信号是否安全
- Visual Studio 2015 错误 C4996"std::_Copy_impl":使用可能不安全的参数进行函数调用
- 如何干净地创建类型安全的枚举参数
- 使用可变参数模板创建传递新对象的更安全方式是否是个好主意
- 错误 C4996:"std::_Copy_impl":使用可能不安全的参数进行函数调用
- 将正在移动的对象的成员作为参数传递是否安全
- 调用需要具有结构的独立参数的 C++ 函数是否安全
- 锁定线程安全队列的移动构造函数的右值参数?