Skip to content

Latest commit

 

History

History
220 lines (152 loc) · 8.06 KB

File metadata and controls

220 lines (152 loc) · 8.06 KB

二十五、重载函数名

在 C++ 中,如果函数具有不同数量的参数或不同的参数类型,则多个函数可以具有相同的名称。对多个函数使用相同的名字被称为重载,在 C++ 中很常见。

过载

所有编程语言都在某种程度上使用重载。例如,大多数语言使用+进行整数加法和浮点加法。有些语言,如 Pascal,对整数除法(div)和浮点除法(/)使用不同的运算符,但其他语言,如 C 和 Java,使用相同的运算符(/)。

C++ 将重载向前推进了一步,允许重载自己的函数名。明智地使用重载可以大大降低程序的复杂性,使程序更容易阅读和理解。

例如,C++ 从标准 C 库中继承了几个计算绝对值的函数:abs接受一个int参数;fabs采取浮点论证;而labs需要一个长整型参数。

Note

不要担心我还没有介绍这些其他类型。对于这个讨论的目的来说,重要的是它们与int不同。下一次探索将开始更仔细地检查它们,所以请耐心等待。

C++ 对于复数也有自己的complex类型,有自己的绝对值函数。然而在 C++ 中,它们都有相同的名字,std::abs。对不同的类型使用不同的名称只会使思维混乱,对代码的清晰性没有任何帮助。

仅举一个例子,sort函数有两种重载形式:

std::sort(start, end);
std::sort(start, end, compare);

(std::ranges::sort功能因使用ranges而不同,这将在探索 47 中解释。)第一个表单按升序排序,用<操作符比较元素,第二个表单通过调用compare比较元素。重载出现在标准库中的许多其他地方。例如,当您创建一个locale对象时,您可以通过不传递参数来复制全局语言环境

std::isalpha('X', std::locale{});

或者通过传递空字符串参数来创建本机区域设置对象

std::isalpha('X', std::locale{""});

重载函数很容易,为什么不直接加入呢?**写一组函数,都命名为 print。**它们都有一个void返回类型并接受各种参数:

  • 一个将一个int作为参数。它将参数打印到标准输出。

  • 另一个需要两个int参数。它将第一个参数打印到标准输出,并将第二个参数用作字段宽度。

  • 另一个将一个vector<int>作为第一个参数,后面跟着三个string_view参数。打印第一个string_view参数,然后是vector的每个元素(通过调用print),元素之间是第二个string_view参数,第三个string_view参数在vector之后。如果vector为空,仅打印第一个和第三个string_view参数。

  • 另一个与vector表单具有相同的参数,但是也采用一个int作为每个vector元素的字段宽度。

使用打印功能编写一个程序来打印矢量。将你的功能和程序与清单 25-1 中我的进行比较。

import <iostream>;
import <string_view>;
import <vector>;

void print(int i)
{
  std::cout << i;
}

void print(int i, int width)
{
  std::cout.width(width);
  std::cout << i;
}

void print(std::vector<int> const& vec,
    int width,
    std::string_view prefix,
    std::string_view separator,
    std::string_view postfix)
{
  std::cout << prefix;

  bool print_separator{false};
  for (auto x : vec)
  {
    if (print_separator)
      std::cout << separator;
    else
      print_separator = true;
    print(x, width);
  }

  std::cout << postfix;
}

void print(std::vector<int> const& vec,
    std::string_view prefix,
    std::string_view separator,
    std::string_view postfix)
{
  print(vec, 0, prefix, separator, postfix);
}

int main()
{
  std::vector<int> data{ 10, 20, 30, 40, 100, 1000, };

  std::cout << "columnar data:\n";
  print(data, 10, "", "\n", "\n");
  std::cout << "row data:\n";
  print(data, "{", ", ", "}\n");
}

Listing 25-1.Printing Vectors by Using Overloaded Functions

C++ 库经常使用重载。例如,您可以通过调用resize成员函数来改变vector的大小。您可以传递一两个参数:第一个参数是vector的新大小。如果您传递第二个参数,它是一个用于新元素的值,以防新的大小大于旧的大小。

data.resize(10);      // if the old size < 10, use default of 0 for new elements
data.resize(20, -42); // if the old size < 20, use -42 for new elements

库作者经常使用重载,但是应用程序程序员很少使用它。通过编写以下函数来练习编写库:

bool is_alpha(char ch)

如果ch是全球语言环境中的字母字符,则返回true;如果没有,返回false

bool is _ alpha(STD::string _ view str)

如果str只包含全球语言环境中的字母字符,则返回true;如果任何字符不是字母,则返回false。如果str为空,则返回true

char to_lower(char ch)

如果可能,将其转换为小写后返回ch;否则,返回ch。使用全球语言环境。

STD::string to _ lower(STD::string _ view str)

str的内容转换成小写字母后,每次返回一个字符。逐字复制任何不能转换成小写的字符。

char to_upper(char ch)

如果可能的话,转换成大写后返回ch;否则,返回ch。使用全球语言环境。

STD::string to _ upper(STD::string _ view str)

str的内容转换成大写后,返回其副本,一次一个字符。逐字复制任何不能转换为大写的字符。

将您的解决方案与我的进行比较,如清单 25-2 所示。

import <algorithm>;
import <iostream>;
import <locale>;
import <ranges>;
import <string>;
import <string_view>;

bool is_alpha(char ch)
{
  return std::isalpha(ch, std::locale{});
}

bool is_alpha(std::string_view str)
{
  return std::ranges::all_of(str, [](char c) { return is_alpha(c); });
}

char to_lower(char ch)
{
  return std::tolower(ch, std::locale{});
}

std::string to_lower(std::string_view str)
{
  auto data{str | std::views::transform([](char c) { return to_lower(c); })};
  return std::string{ std::ranges::begin(data), std::ranges::end(data) };
}

char to_upper(char ch)
{
  return std::toupper(ch, std::locale{});
}

std::string to_upper(std::string str)
{
  for (char& ch : str)
    ch = to_upper(ch);
  return str;
}

int main()
{
  std::string str{};
  while (std::cin >> str)
  {
    if (is_alpha(str))
      std::cout << "alpha\n";
    else
      std::cout << "not alpha\n";
    std::cout << "lower: " << to_lower(str) << "\n"
                 "upper: " << to_upper(str) << '\n';
  }
}

Listing 25-2.Overloading Functions in the Manner of a Library Writer

我通过调用std::ranges::all_of算法实现了to_lower,该算法为一个范围内的每个元素调用一个谓词,如果谓词为所有元素返回 true,则返回 true。如果谓词返回 false,那么 all_of 将停止对该范围的迭代,并立即返回 false。

为了多样化,我实现了完全不同的to_upper。我使用了一个扭转的远程for循环。当循环迭代字符串时,这个循环实际上修改了字符元素。像按引用传递函数参数一样,声明char& ch是对范围内字符的引用。因此,赋值给ch会改变字符串中的字符。注意auto也可以声明一个引用。如果范围中的每个元素都很大,应该使用引用来避免不必要的复制。如果不需要修改元素,使用const参考,如下所示:

for (auto const& big_item : container_full_of_big_things)

另一个区别是to_upper采用一个普通的string作为它的参数。这意味着参数通过值传递,这又意味着编译器在将参数传递给函数时安排复制字符串。该函数需要复制,因此这种技术有助于编译器生成复制参数的最佳代码,并为您节省了编写函数的步骤。这是一个小技巧,但很有用。这项技术在本书的后面会特别有用——所以不要忘记它。

is_alpha字符串函数不修改它的参数,所以它可以使用string_view类型。

重载的一个常见用途是重载不同类型的函数,包括不同的整数类型,如长整型。下一篇文章将研究这些其他类型。