Skip to content

Latest commit

 

History

History
187 lines (170 loc) · 6.47 KB

chapter5.md

File metadata and controls

187 lines (170 loc) · 6.47 KB

第5章 函数

  • 函数原型并不一定必须包含参数名称。这是因为编译期间只需要知道参数的类型,而不是它们的名称。

  • 内联化

    • 如果函数中计算某个值的操作可以在编译期间执行,那么我们可以在语句声明的左边使用关键字constexpr对它进行标记,以告知编译期在编译代码时计算它的值,从而达到优化代码性能的目的。
    • 如果函数值可以在编译过程中计算,则意味着必须在编译期间获知函数中的参数,因此它们必须是字面值。
    • 该函数也必须是一个单行函数。如果不符合这些条件,那么编译器将自行忽略这些声明符。
    • inline当其他代码调用函数时,它可以被放置在函数声明的左边作为一个提示编译器的标记,而不是让编译器在内存中插入一个到该函数的跳转,编译器应该将实际的代码副本放在调用函数中、此外,编译器可以自行忽略该声明符。
  • 返回类型

    • 第一种是在函数名前面指定返回类型。

    • 第二种方法称为函数返回类型后置,需要用户将关键字auto放在函数名前面作为发回来类型,并使用->语法在参数列表后面给出实际的返回值类型。

        inline auto mult(int lhs, int rhs) -> int
        {
        	return lhs*rhs;
        }
      
    • 当函数被模板化并且返回类型可能不明显时,第二种语法很有用。

  • 如果一个函数根本不会返回任何职,可以使用C++11属性[[noreturn]]对它进行标记。编译器可以根据这个属性编写出更高效的代码。

  • 声明异常

      int calculate(int param) throw(overflow_error, other_error)//异常类型列表,逗号分隔
      {
      }
      int calculate(int param) throw(...)//可能抛出任何异常
      {
      }
      int calculate(int param) throw()//不会抛出异常
      {
      }
      //C++11风格,不会抛出异常
      int calculate(int param) noexcept
      {
      }
    
  • 默认参数

    • 默认值是出现在函数定义而不是函数原型中。
  • 可变参数

      #include <iostream>
      #include <cstdarg>
      
      using namespace std;
      
      int sum(int count, ...) {
      	int sum = 0;
      	va_list args;
      	va_start(args, count);
      	while (count--) {
      		int i = va_arg(args, int);
      		sum += i;
      	}
      	va_end(args);
      	return sum;
      }
      
      int main() {
      	cout << sum(0) << endl;
      	cout << sum(6, -6, -5, -4, -3, -2, -1) << endl;
      	cout << sum(3, 10, 20, 30) << endl;
      	return 0;
      }
    
  • 堆栈调用

    • 当调用一个函数时,编译器将为新的函数调用创建一个栈帧,并且它会将元素推送到堆栈上。
    • 这意味着在运行中发起一个函数调用时,在执行函数之前会产生一些内存开销和创建栈帧的性能开销,以及在函数执行完毕后清理方面的性能开销。
    • 如果一个函数时内联的,将不会产生这些开销,因为函数调用将使用当前的栈帧,而不是新建一个。
  • 声明维护堆栈的方式

    • __clrcall
    • __cdecl
    • __stdcall
    • __thiscall
  • 函数重载

  • 声明函数指针

    • 获取函数地址的方法非常简单,只需要使用没有括号的函数名称即可:

        void *pv = get_status;
      
  • 模板函数

      template<typename T, typename U>
      T maximun(T lhs, U rhs)
      {
      	return (lhs > rhs) ? lhs : rhs;
      }
    
  • 专一化模板

      template <typename T> int number_of_byte(T t) {
      	return sizeof(t);
      }
      //专一化模板
      template <> int number_of_byte<const char*>(const char* str) {
      	return strlen(str) + 1;
      }
      
      template<typename T>
      T maximum(T lhs, T rhs) {
      	return (lhs > rhs) ? lhs : rhs;
      }
      //专一化模板
      template <> bool maximum<bool>(bool lhs, bool rhs) = delete;
    
  • 可变参数模板

  • 运算符重载

    • C++提供了关键字operator用于声明运算符函数而不使用函数调用语法,而是使用与运算符相关的语法。
  • 函数对象

    • 头文件<functional>中包含多种可以当做函数对象使用的类型
      • 算数运算: dividesminusmodulusmutipliesnegateplus
      • 位运算:bit_andbit_notbit_orbit_xor
      • 比较运算:equal_togreatergreater_equallessless_equalsnot_equal_to
      • 逻辑运算:logical_andlogical_notlogical_or
  • lambda表达式用在将要使用函数对象的地方创建一个匿名函数对象,这可以让代码更易读。

  • 综合示例

      #include <iostream>
      #include <string>
      #include <windows.h>
      #include <vector>
      #include <tuple>
      #include <iomanip>
      #include <algorithm>
      
      using namespace std;
      
      struct file_size {
      	unsigned int high;
      	unsigned int low;
      };
      
      using file_info = tuple<string, file_size>;
      
      void files_in_folder(const char* folderPath, vector<file_info>& files) {
      	string folder(folderPath);
      	folder += "*";
      	WIN32_FIND_DATAA findfiledata{};
      	void* hFind = FindFirstFileA(folder.c_str(), &findfiledata);
      	cout << findfiledata.cFileName << endl;
      	if (hFind != INVALID_HANDLE_VALUE)
      	{
      		do {
      			string findItem(folderPath); //等价于 string findItem = folderPath;
      			findItem += "";
      			findItem += findfiledata.cFileName;
      			if ((findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
      			{
      				string folder(findfiledata.cFileName);
      				if (folder != "." && folder != "..") {
      					files_in_folder(findItem.c_str(), files);
      				}
      			}
      			else {
      				file_size fs{};
      				fs.high = findfiledata.nFileSizeHigh;
      				fs.low = findfiledata.nFileSizeLow;
      				files.push_back(make_tuple(findItem, fs));
      			}
      		} while (FindNextFileA(hFind, &findfiledata));
      		FindClose(hFind);
      	}
      }
      
      ostream& operator<<(ostream& os, const file_size fs) {
      	int flags = os.flags();
      	unsigned long long ll = fs.low + ((unsigned long long)fs.high << 32);
      	os << hex << ll;
      	os.setf(flags);
      	return os;
      }
      bool operator>(const file_size& lhs, const file_size& rhs)
      {
      	if (lhs.high > rhs.high) return true;
      	if (lhs.high == rhs.high) {
      		if (lhs.low > rhs.low) return true;
      	}
      	return false;
      }
      int main(int argc, char* argv[])
      {
      	//if (argc < 2) return 1;
      	string dir2("D:");
      	vector<file_info> files;
      	cout << dir2.c_str() << endl;
      	files_in_folder(dir2.c_str(), files);
      
      	sort(files.begin(), files.end(),
      		[](const file_info& lhs, const file_info& rhs)
      		{return get<1>(rhs) > get<1>(lhs);
      		});
      	for (auto file : files) {
      		cout << setw(16) << get<1>(file) << " " << get<0>(file) << endl;
      	}
      	return 0;
      }