C++的超20种函数类型分享

简介: C++超20种函数类型:编程语言规定规则,编译器实现预定规则

编程语言规定规则,编译器实现预定规则。

程序员按预定规则编制源程序,编译器、链接器按预定规则编译源程序、链接相关部分为可执行文件,计算机平台加载并运行可执行文件。

编程语言的抽象层次越高,规则背后隐藏的细节越多,背后编译器完成的工作越多。

函数是任何编程语言的最基本和最重要的构件。C++的函数类别更是纷繁复杂:

1 C++11之前
1.1 非成员函数或自由函数
1.2 成员函数
1.3 可变参函数
1.4 重载函数
1.5 静态函数
1.6 内联函数
1.7 运算符重载函数
1.8 只读成员函数
1.9 友元函数
1.10 虚成员函数
1.11 特殊成员函数(构造、析构、拷贝、拷贝构造)
1.12 函数模板、类模板、成员模板
2 C++11
2.1 可变参模板函数
2.2 返回值类型后置模板函数
2.3 带override 说明符成员函数
2.4 带final说明符成员函数
2.5 增加的特殊成员函数(移动、移动赋值)
2.6 default 成员函数(显式预置的函数定义)
2.7 delete 成员函数(显式弃置的函数定义)
2.8 delete 其他函数
2.9 lambda 函数
3 C++14
3.1 返回类型自动推导函数
3.2 泛型lambda
4 C++17
4.1 扩展 lambda
4.2 向 lambda 传递 this 的拷贝
4.3 异常声明作为函数类型的一部分
5 C++20
5.1 consteval 函数(立即函数)
5.2 简短的函数模板
5.3 lambda 函数模板
5.4 constexpr 虚成员函数
5.5 协程
6 未来
6.1 Contracts 合约
1 C++11之前
1.1 非成员函数或自由函数
int add(int a, int b)
{
return a + b;
}
函数(自由函数)是编程语言构建程序模块的根本,一切其它类型的函数都会被处理为自由函数。

1.2 成员函数
它们是类/结构的一部分。这些也被称为方法(就像在大多数其他面向对象的编程语言中一样),尽管这个术语在C++标准中没有被使用。下面是一个例子:

class Calc
{
int a,int b;
public:
Calc(int a,int b):a(a),b(b){}
int add();
};
int Calc::add()
{
return this->a + this->b;
}
成员函数最终会处理成自由函数,编译器背后的行为有:

① 添加指向对象的this指针做函数参数;

② 添加类名命名空间;

1.3 可变参函数

include

include

int sum(int count, ...); //原型中使用省略号
int sum(int count, ...){
va_list ap; // typedef char * va_list;
va_start(ap,count); // 确定第一个参数后参数的内存位置
int sum = 0;
for(int i = 0; i < count; i++)
sum += va_arg(ap, int); // 根据可变参数类型及前一个参数的位置来确定指针偏移并做解引用
va_end(ap);
return sum;
}
main(){
printf("%d",sum(3,11,22,33)); // 66
getchar();
}
可变参函数的实现思路:

根据函数调用约定和第一个参数压入栈的位置,及第一个参数提供的信息,来确定其它参数在栈帧上的位置,其实现通过一个字节指针来实现。第一个参数可以是一个确定可变参数数量的整型变量,也可以是一个格式化字符串常量。

以下是实现可变参数函数的5个宏:
//代码效果参考:http://www.zidongmutanji.com/zsjx/421117.html

define _VA_LIST_DEFINED

define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) // 考虑了内存对齐

define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

define va_arg(ap,t) ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

define va_end(ap) ( ap = (va_list)0 )

1.4 重载函数
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
重载函数无它,只是名称修饰(name mangling)而已。

1.5 静态函数

include

class Account{
double balance;
public:
static double interest;
Account(int bal):balance(bal){}
static void setInterest(double i)
{
interest = i;
}
double getBalance(){
return balance;
}
};
double Account::interest = 0.3;
main(){
Account ac(3000);
ac.setInterest(0.4);
printf("%lf\n",ac.getBalance()*(1+Account::interest));//4200
getchar();
}
对于结构化编程来说,要尽量避免使用全局变量,面向对象编程也是如此。

对于类来说,有些类的数据独立于对象而存在(类共享),自然,这类数据无须类实例化即可使用,这类数据称为类的静态数据成员(需要在类外声明并初始化),用static修饰,静态数据成员通过一类没有隐含的this指针的成员函数来处理,这类成员函数就是静态成员函数(不能处理非静态数据成员,因为没有隐含的this指针)。

1.6 内联函数
inline int add(int a, int b) {return a + b;}
struct math
{
inline int add(int a, int b);
}
int match::add(int a, int b) {return a + b;}
函数实现了代码的模块化,但函数的调用是一个较复杂的过程,其开销(overhead)总是存在的。在C中,折衷的做法是使用参数宏来代替短小简单的函数,但参数宏是没有数据类型使用的,自然也谈不上数据类型检查。C++的做法是使用用inline修饰的内联函数,既有类型检查,也能在编译前在内联函数调用处做代码展开,避免函数调用开销。

1.7 运算符重载函数

include

include

std::string operator+(std::string const & txt, int n)
{
return txt + std::to_string(n); //C++11
}

int main(){
const std::string s = "123";
int n = 456;
std::string str =s+n; // operator+(s,n);
std::cout<<str<<std::endl;
getchar();
return 0;
}
在C++中,运算符可以理解为一类特殊的函数,函数名为关键字operator+运算符。

1.8 只读成员函数
class wrapper
{
public:
wrapper(int a): value(a) {}
int get() const {return value
;}
private:
int value_;
};
只读成员函数表示其只读类的数据成员。

1.9 友元函数
class math
{
int a,b;
public:
math(int a,int b):a(a),b(b){}
friend int add(math m);
};
int add(math m)
{
return m.a + m.b;
}
友元函数是一种特殊的成员函数,能获得访问访问类的private数据成员的权限。

1.10 虚成员函数
struct A
{
virtual void f() { std::cout << "A::f()\n"; }
};
struct B : public A
{
virtual void f() { std::cout << "B::f()\n"; }
};
虚成员函数用于继承层次中函数重写,实现同一接口适用继承层次中的不同对象,呈现不同行为。编译器背后的行为有:

① 为继承层次中的每个类建立一个虚函数表;

② 在基类中插入一个指向虚函数表的指针。

③ 虚函数调用时会根据其所在类指向虚函数表的指针来动态匹配并调用虚函数。

1.11 特殊成员函数(构造、析构、拷贝、拷贝复制)
class wrapper
{
public:
wrapper() : value(0) {} // 构造函数,用于数据成员初始化
wrapper(wrapper const & other) { // 拷贝构造,使用另一个对象的状态来初始化数据成员
value
= other.value; }
wrapper& operator=(wrapper const & other) { // 拷贝函数,,使用另一个对象的状态来更新数据成员
if(this != &other) {
value
= other.value;} }
~wrapper() {} // 析构函数,需要时,释放资源资源,如堆内存
private:
int value
;
};
抽象数据类型通过特殊成员函数实现RAII(Resource Acquisition Is Initialization)。

1.12 函数模板、类模板、成员模板
template
T add(T a, T b)
{
return a + b;
}
template
class math1
{
public:
T add(T a, T b)
{
return a + b;
}
};
class math2
{
public:
template
T add(T a, T b)
{
return a + b;
}
};
模板是类型的参数化,模板的具体化由编译器完成。
//代码效果参考:http://www.zidongmutanji.com/bxxx/553946.html

2 C++11
2.1 可变参模板函数

template
T add(T a, T b)
{
return a + b;
}
template
T add(T t, Ts ... rest)
{
return t + add(rest...);
}
可变参模板函数是可变参函数与模板函数两种语法机制的结合。

2.2 返回值类型后置模板函数
auto add(int a, int b) -> int
{
return a + b;
}
template
auto add(T const & a, U const & b) -> decltype(a + b)
{
return a + b;
}
constexpr 函数
template
constexpr T add(T a, T b)
{
return a + b;
}
int main()
{
int arr[add(1,2)] = {1,2,3}; // [1]
int a, b;
std::cin >> a >> b;
std::cout << add(a, b) << '\n'; // [2]
}
其实质也是一种数据类型声明和数据类型推断。

2.3 带override 说明符成员函数
struct A
{
virtual void f(int) {}
virtual void g() {}
};
struct B : public A
{
void f(int) override {} // OK
void g(char) override {} // error, g() does not override anything
};
显示声明虚函数重写。

2.4 带final说明符成员函数
struct A
{
virtual void f() {}
};
struct B : public A
{
void f() override (final {}
};
struct C : public B
{
void f() override {} // error, f cannot be overridden anymore
};
显式声明虚函数不要被重写。

2.5 增加的特殊成员函数(移动构造、移动赋值)
struct buffer
{
buffer() // default constructor
:data(nullptr), size(0)
{}

explicit buffer(sizet size) // constructor
:data
(new char[size]), size_(size)
{}

~buffer() // destructor
{
delete [] data_;
}

buffer(buffer const & other) // copy constructor
: data(new char[other.size])
, size(other.size)
{
std::memcpy(data, other.data, size_);
}

buffer& operator=(buffer const & other) // copy assignment operator
{
if(this != &other)
{
delete [] data;
data
= new char[other.size];
size
= other.size;
std::memcpy(data
, other.data, size);
}

   return *this;

}

buffer(buffer&& other) // move constructor
: data(std::move(other.data))
, size(other.size)
{
other.data = nullptr;
other.size
= 0;
}

buffer& operator=(buffer&& other) // move assignment operator
{
if(this != &other)
{
delete [] data;
data
= std::move(other.data);
size
= other.size;
other.data
= nullptr;
other.size_ = 0;
}
return *this;
}
//代码效果参考:http://www.zidongmutanji.com/bxxx/243158.html

private:
char* data_;
sizet size;
};
int main()
{
buffer b1;
buffer b2(10);
buffer b3 = b2;
buffer b4 = std::move(b3);
}移动构造、移动赋值
移动构造、移动赋值成员函数对于临时对象,使用移动而不是复制,具有更高的效率。

2.6 default 成员函数(显式预置的函数定义)
struct foo
{
foo(int) {} // user-defined constructor
foo() = default; // compiler generated default constructor
};
显式要求编译器生成一个默认构造函数。

2.7 delete 成员函数(显式弃置的函数定义)
struct noncopyable
{
noncopyable() = default;
noncopyable(noncopyable const &) = delete;
noncopyable& operator=(noncopyable const &) = delete;
};
delete显式要求编译器不要生成对应类型的成员函数。

2.8 delete 其他函数
template
T add(T a, T b)
{
return a + b;
}
template <>
int add(int a, int b) = delete;
int main()
{
add(1, 2); // error, this specialization is deleted
}
显式要求编译器不要生成特定参数类型的模板函数。

2.9 lambda 函数
int main()
{
auto add = { return a + b; };
add(1, 2);
}
lambda函数可以理解为函数对象的语法糖。

3 C++14
3.1 返回类型自动推导函数
auto add(int a, int b)
{
return a + b;
}
template
auto add(T a, U b)
{
return a + b;
}
3.2 泛型lambda
int main()
{
using namespace std::string_literals;

auto add = [](auto a, auto b) {return a + b;};

add(1, 2);
add(1.0, 2.0);
add("1"s, "2"s);

}
4 C++17
4.1 扩展 lambda
constexpr lambda,自从 C++17 起,lambda表达式会尽可能的隐式声明 constexpr。

auto squared = { // 自 从C++17起 隐 式constexpr
return valval;
};
std::array a; // 自 从C++17起OK => std::array
4.2向 lambda 传递 this 的拷贝
class C {
private:
std::string name;
public:
void foo() {
auto l1 = [
this] { std::cout << name << '\n'; };
}
};
4.3 异常声明作为函数类型的一部分
这里,派生类中的成员函数 foo() 和基类中的 foo() 类型不同所以不能重载。这段代码不能通过编译,即使 没有 override 修饰符代码也不能编译,因为我们不能用更宽松的异常声明重载。

class Base {
public:
virtual void foo() noexcept;
};
class Derived : public Base {
public:
void foo() override; // ERROR: 不 能 重 载
//...
};
5 C++20
5.1 consteval 函数(立即函数)
consteval int add(int const a, int const b)
{
return a + b;
}
int main()
{
constexpr int s1 = add(1, 2); // OK, compile-time evaluation
int a = 12, b = 66;
const int s2 = add(a, b); // error

using fptr = int(int, int);
fptr* padd = add; // error
}
5.2 简短的函数模板
auto add(auto a, auto b)
{
return a + b;
}
5.3 lambda 函数模板
auto add = {return a + b;};
5.4 constexpr 虚成员函数
struct magic
{
constexpr virtual int def() const { return 0; }
};
struct programming_magic : public magic
{
constexpr int def() const (override { return 42; }
};
constexpr int initval(magic const & m)
{
return m.def() + 1;
}
int main()
{
constexpr programming_magic pm;
int arr[initval(pm)] = {0};
}
5.5 协程
协程,这个是C++20标准的主要特征之一。coroutine是一个具有暂停和恢复能力的函数。不幸的是,C++20只定义了一个执行程序的框架,但并没有定义任何满足这种要求的程序类型。这意味着,我们需要自己编写或者依靠第三方库来实现。这样一个库就是cppcoro库。在C++20中,有三个新的关键字,用于coroutine:co_await,co_return,和co_yield。如果一个函数使用了这三个中的一个,它就会成为一个循环程序。

co_await操作符,用于暂停执行,直到重新开始。

co_return关键字,完成执行并可选返回一个值

co_yield关键字用于暂停执行并返回一个值

include

cppcoro::generator produce_items()
{
while (true)
{
auto v = rand();
using namespace std::string_literals;
auto i = "item "s + std::to_string(v);
print_time();
std::cout << "produced " << i << '\n';
co_yield i;
}
}

include

cppcoro::task<> consume_items(int const n)
{
int i = 1;
for(auto const& s : produce_items())
{
print_time();
std::cout << "consumed " << s << '\n';
if (++i > n) break;
}
co_return;
}

相关文章
|
2天前
|
C++
C++一分钟之-类型别名与using声明
【7月更文挑战第20天】在C++中,类型别名和`using`声明提升代码清晰度与管理。类型别名简化复杂类型,如`using ComplexType = std::vector&lt;std::shared_ptr&lt;int&gt;&gt;;`,需注意命名清晰与适度使用。`using`声明引入命名空间成员,避免`using namespace std;`全局污染,宜局部与具体引入,如`using math::pi;`。恰当应用增强代码质量,规避常见陷阱。
19 5
|
7天前
|
安全 编译器 C++
C++一分钟之-模板元编程实例:类型 traits
【7月更文挑战第15天】C++的模板元编程利用编译时计算提升性能,类型traits是其中的关键,用于查询和修改类型信息。文章探讨了如何使用和避免过度复杂化、误用模板特化及依赖特定编译器的问题。示例展示了`is_same`类型trait的实现,用于检查类型相等。通过`add_pointer`和`remove_reference`等traits,可以构建更复杂的类型转换逻辑。类型traits增强了代码效率和安全性,是深入C++编程的必备工具。
24 11
|
3天前
|
C++
C++ string中的函数和常用用法
C++ 中string中的函数和常用用法
11 4
|
18天前
|
C++ 开发者
C++一分钟之-概念(concepts):C++20的类型约束
【7月更文挑战第4天】C++20引入了Concepts,提升模板编程的类型约束和可读性。概念定义了模板参数需遵循的规则。常见问题包括过度约束、约束不完整和重载决议复杂性。避免问题的关键在于适度约束、全面覆盖约束条件和理解重载决议。示例展示了如何用Concepts限制模板函数接受的类型。概念将增强模板的安全性和灵活性,但需谨慎使用以防止错误。随着C++的发展,Concepts将成为必备工具。
34 2
|
19天前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
1天前
|
编译器 C++
C++中的auto类型有什么用途
C++中的auto类型有什么用途
|
16天前
|
C++ 开发者
C++一分钟之-概念(concepts):C++20的类型约束
【7月更文挑战第6天】C++20引入了Concepts,提升模板编程的精确性和可读性。概念允许设定模板参数的编译时约束。常见问题包括过度约束、不完整约束及重载决议复杂性。要避免这些问题,需适度约束、全面覆盖约束条件并理解重载决议。示例展示了如何定义和使用`Incrementable`概念约束函数模板。概念是C++模板编程的强大力量,但也需谨慎使用以优化效率和代码质量。
39 0
|
19天前
|
算法 C++ 容器
|
19天前
|
存储 编译器 程序员