《C++ Primer》导学系列:第 6 章 - 函数

6.1 函数基础

6.1.1 基本概念

函数是C++程序的基本组成单元,用于将代码组织成可以复用的模块。函数通过函数名进行调用,并且可以接受参数和返回值。函数的定义包括函数头和函数体,其中函数头描述了函数的接口,函数体包含了具体的实现代码。

函数的定义

函数的定义通常包括返回类型、函数名、参数列表和函数体。函数头声明了函数的返回类型和参数类型,函数体则包含实际执行的代码。

示例代码
#include <iostream>// 函数定义
int add(int a, int b) {return a + b;  // 返回 a 和 b 的和
}int main() {int result = add(3, 4);  // 调用 add 函数std::cout << "Result: " << result << std::endl;  // 输出结果return 0;
}

在这个示例中,add函数接受两个整数参数,并返回它们的和。main函数调用add函数并输出结果。

6.1.2 函数声明

在使用函数之前,必须声明函数。函数声明告诉编译器函数的名称、返回类型和参数类型,但不需要具体实现。函数声明通常放在头文件中,函数定义放在源文件中。

示例代码
#include <iostream>// 函数声明
int add(int a, int b);int main() {int result = add(3, 4);  // 调用 add 函数std::cout << "Result: " << result << std::endl;  // 输出结果return 0;
}// 函数定义
int add(int a, int b) {return a + b;  // 返回 a 和 b 的和
}

在这个示例中,add函数的声明在main函数之前,定义在main函数之后。

6.1.3 形参和实参

函数在定义时使用形参(形式参数)来指定参数类型和名称,在调用时使用实参(实际参数)来传递具体的值。形参在函数定义时声明,实参在函数调用时提供。

示例代码
#include <iostream>// 函数定义
void printSum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;  // 输出 a 和 b 的和
}int main() {int x = 5;int y = 7;printSum(x, y);  // 调用 printSum 函数,x 和 y 是实参return 0;
}

在这个示例中,printSum函数接受两个整数形参ab,在调用时传递具体的实参xy

6.1.4 返回类型

函数可以返回一个值,返回类型可以是任何基本类型、指针、引用、类类型等。如果函数不返回值,则返回类型为void

示例代码
#include <iostream>// 函数定义,返回类型为 int
int multiply(int a, int b) {return a * b;  // 返回 a 和 b 的乘积
}// 函数定义,返回类型为 void
void printMessage() {std::cout << "Hello, World!" << std::endl;
}int main() {int result = multiply(3, 4);  // 调用 multiply 函数std::cout << "Result: " << result << std::endl;  // 输出结果printMessage();  // 调用 printMessage 函数return 0;
}

在这个示例中,multiply函数返回两个整数的乘积,而printMessage函数不返回值。

6.1.5 函数调用

函数调用是通过函数名和参数列表来实现的。函数调用时,实参的值被传递给形参,并执行函数体中的代码。

示例代码
#include <iostream>// 函数定义
int subtract(int a, int b) {return a - b;  // 返回 a 和 b 的差
}int main() {int a = 10;int b = 3;int result = subtract(a, b);  // 调用 subtract 函数std::cout << "Result: " << result << std::endl;  // 输出结果return 0;
}

在这个示例中,subtract函数接受两个整数参数并返回它们的差,在main函数中调用并输出结果。

重点与难点分析

重点

  1. 函数定义与声明:掌握函数的定义和声明方法,理解函数头和函数体的组成。
  2. 形参和实参:理解形参和实参的概念及其在函数定义和调用中的作用。
  3. 返回类型:掌握函数返回类型的定义方法,理解void类型的应用场景。

难点

  1. 函数调用:初学者需要通过实践掌握函数调用的语法,理解实参与形参的对应关系。
  2. 返回类型的应用:在复杂的程序中,合理设计和使用函数的返回类型,特别是指针和引用类型的返回值。

练习题解析

练习6.1:编写一个函数,接受一个整数参数并打印该整数的平方。

    • 示例代码
#include <iostream>void printSquare(int x) {std::cout << "Square: " << x * x << std::endl;
}int main() {printSquare(4);return 0;
}

练习6.2:编写一个函数,接受一个字符参数并返回该字符的大写形式。

    • 示例代码
#include <iostream>
#include <cctype>char toUpperCase(char c) {return std::toupper(c);
}int main() {char c = 'a';char result = toUpperCase(c);std::cout << "Uppercase: " << result << std::endl;return 0;
}

练习6.3:编写一个void函数,不接受任何参数,并打印一条问候消息。

    • 示例代码
#include <iostream>void greet() {std::cout << "Hello, welcome to C++ programming!" << std::endl;
}int main() {greet();return 0;
}

总结与提高

本节总结

  1. 学习了函数的基本概念,掌握了函数的定义、声明和调用方法。
  2. 理解了形参和实参的作用,掌握了返回类型的定义和应用。
  3. 通过示例代码和练习题,加深了对函数基础知识的理解和应用。

提高建议

  1. 多练习函数的定义和调用:通过编写各种包含函数的程序,熟悉函数的定义、声明和调用方法。
  2. 深入理解形参和实参:通过实践掌握形参和实参的对应关系,确保函数调用时参数传递正确。
  3. 合理设计函数的返回类型:在编写复杂程序时,合理设计和使用函数的返回类型,提高代码的可读性和维护性。

6.2 参数传递

6.2.1 传值参数

按值传递是将实参的值复制一份传递给函数的形参。函数对形参的修改不会影响实参的值。按值传递适用于传递基本数据类型(如int, double等)和小型的结构体或类对象。

示例代码
#include <iostream>void increment(int x) {++x;  // 仅修改形参 x 的值std::cout << "Inside function: " << x << std::endl;
}int main() {int a = 5;increment(a);  // 按值传递,实参 a 的值不会改变std::cout << "Outside function: " << a << std::endl;return 0;
}

在这个示例中,increment函数按值传递参数x,修改形参不会影响实参a

6.2.2 传引用参数

按引用传递是将实参的引用传递给函数的形参,函数对形参的修改将直接影响实参。按引用传递适用于需要在函数内部修改实参值的情况,或者传递较大的对象以避免不必要的复制开销。

示例代码
#include <iostream>void increment(int &x) {++x;  // 修改形参 x 的值,会影响实参std::cout << "Inside function: " << x << std::endl;
}int main() {int a = 5;increment(a);  // 按引用传递,实参 a 的值会改变std::cout << "Outside function: " << a << std::endl;return 0;
}

在这个示例中,increment函数按引用传递参数x,修改形参会影响实参a

6.2.3 const形参和实参

为了防止函数修改传递的实参,可以使用常量引用(const reference)。常量引用既避免了按值传递的复制开销,又保证了实参在函数内部不被修改。

示例代码
#include <iostream>void printValue(const int &x) {std::cout << "Value: " << x << std::endl;// 不能修改 x 的值
}int main() {int a = 5;printValue(a);  // 按常量引用传递,实参 a 的值不会改变return 0;
}

在这个示例中,printValue函数按常量引用传递参数x,保证形参不会修改实参a

6.2.4 数组形参

在C++中,数组作为函数参数时,会退化为指针。这意味着传递的是数组首元素的地址,而不是整个数组的值。

示例代码
#include <iostream>void printArray(int arr[], int size) {for (int i = 0; i < size; ++i) {std::cout << arr[i] << " ";}std::cout << std::endl;
}int main() {int myArray[] = {1, 2, 3, 4, 5};int size = sizeof(myArray) / sizeof(myArray[0]);printArray(myArray, size);  // 按指针传递数组return 0;
}

在这个示例中,printArray函数接受一个数组指针和数组大小,并打印数组的元素。

6.2.5 main函数的形参

main函数可以接受两个参数,通常用于命令行参数的传递。参数argc表示参数的个数,参数argv是一个字符指针数组,包含传递的参数。

示例代码
#include <iostream>int main(int argc, char *argv[]) {std::cout << "Number of arguments: " << argc << std::endl;for (int i = 0; i < argc; ++i) {std::cout << "Argument " << i << ": " << argv[i] << std::endl;}return 0;
}

在这个示例中,main函数接受命令行参数并打印出来。

6.2.6 含有可变参数的函数

C++提供了可变参数模板(variadic templates)来处理参数数量不确定的情况。通过使用省略号(...)表示可变参数列表。

示例代码
#include <iostream>
#include <cstdarg>void printValues(const char *fmt, ...) {va_list args;va_start(args, fmt);while (*fmt != '\0') {switch (*fmt++) {case 'i': // intstd::cout << va_arg(args, int) << " ";break;case 'd': // doublestd::cout << va_arg(args, double) << " ";break;case 'c': // charstd::cout << static_cast<char>(va_arg(args, int)) << " ";break;}}va_end(args);std::cout << std::endl;
}int main() {printValues("icd", 42, 'a', 3.14);return 0;
}

在这个示例中,printValues函数使用可变参数列表处理不同类型和数量的参数。

重点与难点分析

重点

  1. 按值传递和按引用传递:掌握按值传递和按引用传递的基本概念和区别,理解它们在参数传递中的应用场景。
  2. const形参和实参:理解常量引用的用法和优势,特别是在防止修改实参时的应用。
  3. 数组形参:掌握数组作为函数参数时的传递方式,理解数组退化为指针的概念。
  4. main函数的形参:理解main函数接受命令行参数的机制及其应用。
  5. 含有可变参数的函数:掌握可变参数函数的定义和使用方法,理解可变参数模板的应用。

难点

  1. 参数传递方式的选择:初学者需要通过实践掌握不同参数传递方式的选择,根据具体情况选择最合适的传递方式。
  2. 数组形参的应用:在使用数组形参时,理解数组退化为指针的影响及其在函数中的应用。
  3. 可变参数函数的设计:在设计可变参数函数时,确保参数类型和数量的正确处理,避免潜在的错误。

练习题解析

  1. 练习6.5:编写一个函数,按值传递一个整数参数,并尝试修改该参数的值。
    • 示例代码
#include <iostream>void modifyValue(int x) {x = 10;std::cout << "Inside function: " << x << std::endl;
}int main() {int a = 5;modifyValue(a);  // 按值传递,实参 a 的值不会改变std::cout << "Outside function: " << a << std::endl;return 0;
}
  1. 练习6.6:编写一个函数,按引用传递一个整数参数,并修改该参数的值。
    • 示例代码
#include <iostream>void modifyValue(int &x) {x = 10;std::cout << "Inside function: " << x << std::endl;
}int main() {int a = 5;modifyValue(a);  // 按引用传递,实参 a 的值会改变std::cout << "Outside function: " << a << std::endl;return 0;
}
  1. 练习6.7:编写一个函数,按常量引用传递一个字符串参数,并尝试修改该参数的值。
    • 示例代码
#include <iostream>
#include <string>void printString(const std::string &str) {std::cout << "String: " << str << std::endl;// str = "New String";  // 错误:不能修改常量引用
}int main() {std::string s = "Hello, World!";printString(s);  // 按常量引用传递,实参 s 的值不会改变return 0;
}
  1. 练习6.8:编写一个函数,按值传递指针参数,修改指针指向的值。
    • 示例代码
#include <iostream>void modifyPointer(int *p) {*p = 10;std::cout << "Inside function: " << *p << std::endl;
}int main() {int a = 5;modifyPointer(&a);  // 按值传递指针,实参 a 的值会改变std::cout << "Outside function: " << a << std::endl;return 0;
}
  1. 练习6.9:编写一个函数,按引用传递指针参数,修改指针的地址。
    • 示例代码
#include <iostream>void setPointer(int *&p, int *newAddress) {p = newAddress;
}int main() {int a = 5;int b = 10;int *ptr = &a;setPointer(ptr, &b);  // 修改 ptr 使其指向 bstd::cout << "Pointer now points to: " << *ptr << std::endl;return 0;
}
  1. 练习6.10:编写一个main函数,接受命令行参数并输出这些参数。
    • 示例代码
#include <iostream>int main(int argc, char *argv[]) {std::cout << "Number of arguments: " << argc << std::endl;for (int i = 0; i < argc; ++i) {std::cout << "Argument " << i << ": " << argv[i] << std::endl;}return 0;
}
  1. 练习6.11:编写一个含有可变参数的函数,接受不同类型的参数并输出它们的值。
    • 示例代码
#include <iostream>
#include <cstdarg>void printValues(const char *fmt, ...) {va_list args;va_start(args, fmt);while (*fmt != '\0') {switch (*fmt++) {case 'i': // intstd::cout << va_arg(args, int) << " ";break;case 'd': // doublestd::cout << va_arg(args, double) << " ";break;case 'c': // charstd::cout << static_cast<char>(va_arg(args, int)) << " ";break;}}va_end(args);std::cout << std::endl;
}int main() {printValues("icd", 42, 'a', 3.14);return 0;
}

总结与提高

本节总结

  1. 学习了参数传递的多种方式,包括按值传递、按引用传递、常量引用、数组形参、main函数的形参以及含有可变参数的函数。
  2. 通过示例代码和练习题,深入理解了不同参数传递方式的应用场景和实现方法。
  3. 掌握了参数传递中的关键概念和技术,能够在实际编程中灵活运用这些知识。

提高建议

  1. 多练习不同方式的参数传递:通过编写各种包含不同参数传递方式的程序,熟悉按值传递、按引用传递和常量引用的用法。
  2. 深入理解数组形参和可变参数:通过实践掌握数组形参和可变参数的使用方法,确保在实际应用中正确处理这些参数。
  3. 合理选择参数传递方式:在编写复杂程序时,根据具体情况选择最合适的参数传递方式,提高代码的效率和可读性。

6.3 返回类型和return语句

6.3.1 返回类型

函数的返回类型是函数在执行完毕后返回给调用者的值的类型。C++允许函数返回基本类型、指针、引用、类类型、数组(通过指针)等。如果函数不返回值,其返回类型为void

基本语法
returnType functionName(parameters) {// 函数体return value;  // 返回值
}

6.3.2 返回值的基本类型

函数可以返回基本数据类型(如int, double等)。在函数体中,使用return语句将值返回给调用者。

示例代码
#include <iostream>int add(int a, int b) {return a + b;  // 返回整数
}double multiply(double x, double y) {return x * y;  // 返回浮点数
}int main() {int sum = add(3, 4);double product = multiply(2.5, 4.0);std::cout << "Sum: " << sum << std::endl;std::cout << "Product: " << product << std::endl;return 0;
}

在这个示例中,add函数返回两个整数的和,multiply函数返回两个浮点数的乘积。

6.3.3 返回引用类型

函数可以返回引用类型。当函数返回引用时,调用者接收到的实际上是原对象的引用,而不是一个新的对象。这对于避免拷贝大对象和实现链式调用很有用。

示例代码
#include <iostream>int& getElement(int arr[], int index) {return arr[index];  // 返回数组元素的引用
}int main() {int myArray[5] = {1, 2, 3, 4, 5};getElement(myArray, 2) = 10;  // 修改数组第三个元素的值for (int i = 0; i < 5; ++i) {std::cout << myArray[i] << " ";}std::cout << std::endl;return 0;
}

在这个示例中,getElement函数返回数组元素的引用,允许调用者直接修改数组元素的值。

6.3.4 返回指针类型

函数可以返回指针类型。返回指针时,需要注意指针的生命周期,确保指针指向的内存在函数返回后仍然有效。

示例代码
#include <iostream>int* findMax(int* a, int* b) {return (*a > *b) ? a : b;  // 返回指向较大值的指针
}int main() {int x = 10, y = 20;int* max = findMax(&x, &y);std::cout << "Max value: " << *max << std::endl;return 0;
}

在这个示例中,findMax函数返回指向较大值的指针。

6.3.5 返回void类型

如果函数不需要返回值,可以将返回类型定义为void。这样的函数通常用于执行一些操作而不返回结果。

示例代码
#include <iostream>void printMessage() {std::cout << "Hello, World!" << std::endl;  // 不返回值
}int main() {printMessage();return 0;
}

在这个示例中,printMessage函数不返回值。

重点与难点分析

重点

  1. 返回类型的选择:掌握基本类型、引用类型、指针类型和void类型的返回方法及其适用场景。
  2. return语句的使用:理解return语句的作用,掌握在不同返回类型函数中的使用方法。

难点

  1. 返回引用类型的安全性:初学者需要理解返回引用类型时的安全性问题,避免返回局部变量的引用。
  2. 返回指针类型的生命周期管理:掌握返回指针类型时的生命周期管理,确保返回的指针在函数返回后仍然有效。

练习题解析

  1. 练习6.12:编写一个函数,返回两个整数中的较大值。
    • 示例代码
#include <iostream>int max(int a, int b) {return (a > b) ? a : b;
}int main() {int x = 10, y = 20;std::cout << "Max: " << max(x, y) << std::endl;return 0;
}
  1. 练习6.13:编写一个函数,返回字符串的引用,并修改该字符串的值。
    • 示例代码
#include <iostream>
#include <string>std::string& modifyString(std::string &str) {str += " modified";return str;
}int main() {std::string s = "Original";std::cout << modifyString(s) << std::endl;return 0;
}
  1. 练习6.14:编写一个函数,返回指向数组元素的指针。
    • 示例代码
#include <iostream>int* findElement(int arr[], int size, int value) {for (int i = 0; i < size; ++i) {if (arr[i] == value) {return &arr[i];}}return nullptr;
}int main() {int arr[] = {1, 2, 3, 4, 5};int size = sizeof(arr) / sizeof(arr[0]);int* elem = findElement(arr, size, 3);if (elem) {std::cout << "Element found: " << *elem << std::endl;} else {std::cout << "Element not found." << std::endl;}return 0;
}
  1. 练习6.15:编写一个void函数,打印一个消息,并在满足特定条件时提前返回。
    • 示例代码
#include <iostream>void checkEven(int x) {if (x % 2 == 0) {std::cout << x << " is even." << std::endl;return;}std::cout << x << " is odd." << std::endl;
}int main() {checkEven(10);checkEven(7);return 0;
}

总结与提高

本节总结

  1. 学习了函数的返回类型,掌握了返回基本类型、引用类型、指针类型和void类型的方法。
  2. 理解了return语句的作用,掌握了在不同返回类型函数中的使用方法。
  3. 通过示例代码和练习题,加深了对函数返回类型和return语句的理解和应用。

提高建议

  1. 多练习不同返回类型的函数:通过编写各种包含不同返回类型的函数,熟悉返回基本类型、引用类型、指针类型和void类型的方法。
  2. 深入理解返回引用和指针的安全性:通过实践掌握返回引用和指针的安全性,避免返回局部变量的引用,确保返回的指针在函数返回后仍然有效。
  3. 合理使用return语句:在编写函数时,合理使用return语句,提高代码的可读性和维护性。

6.4 函数重载

6.4.1 基本概念

函数重载是指在同一作用域内定义多个具有相同名称但参数列表不同的函数。重载函数的参数列表必须在参数的数量或类型上有所不同,以使编译器能够根据调用时提供的实参来选择合适的函数。函数重载使程序设计更加灵活和易于维护。

6.4.2 定义重载函数

重载函数是通过定义多个同名但参数列表不同的函数来实现的。编译器在调用重载函数时,根据传递的参数类型和数量来决定调用哪个版本的函数。

示例代码
#include <iostream>// 重载函数定义
void print(int a) {std::cout << "Integer: " << a << std::endl;
}void print(double b) {std::cout << "Double: " << b << std::endl;
}void print(const std::string &c) {std::cout << "String: " << c << std::endl;
}int main() {print(10);           // 调用第一个 print 函数print(3.14);         // 调用第二个 print 函数print("Hello");      // 调用第三个 print 函数return 0;
}

在这个示例中,定义了三个名为print的重载函数,分别接受intdoublestd::string类型的参数。

6.4.3 参数类型转换

在函数重载中,编译器可以进行参数类型转换,以找到最匹配的函数。默认参数和类型提升(如从intdouble)也会影响重载函数的选择。

示例代码
#include <iostream>void display(int a) {std::cout << "Integer: " << a << std::endl;
}void display(double b) {std::cout << "Double: " << b << std::endl;
}int main() {display(10);    // 调用 display(int)display(3.14);  // 调用 display(double)display('A');   // 类型转换,调用 display(int)return 0;
}

在这个示例中,字符'A'被转换为int类型,因此调用了display(int)函数。

6.4.4 默认参数与重载

在定义重载函数时,可以为某些参数指定默认值。如果某个重载函数与带默认参数的函数存在二义性,编译器将无法确定调用哪个函数。

示例代码
#include <iostream>void show(int a, int b = 10) {std::cout << "a: " << a << ", b: " << b << std::endl;
}void show(double a) {std::cout << "a: " << a << std::endl;
}int main() {show(5);      // 调用 show(int, int),使用默认参数show(3.14);   // 调用 show(double)return 0;
}

在这个示例中,show(5)调用了带有默认参数的show(int, int)函数,而show(3.14)调用了show(double)函数。

6.4.5 函数重载的限制

函数重载有一些限制条件,例如,不能仅通过返回类型的不同来重载函数。函数签名(函数名和参数列表)必须在参数的数量或类型上有所区别。

错误示例
#include <iostream>// 错误:仅通过返回类型不同来重载函数
int getValue() {return 10;
}double getValue() {return 3.14;
}int main() {std::cout << getValue() << std::endl;  // 编译错误return 0;
}

在这个示例中,两个getValue函数仅通过返回类型不同来区分,违反了函数重载的规则,导致编译错误。

6.4.6 重载与命名空间

在不同的命名空间中,可以定义同名的重载函数。命名空间提供了一个机制,用于组织代码并避免命名冲突。

示例代码
#include <iostream>namespace FirstNamespace {void func() {std::cout << "Inside FirstNamespace" << std::endl;}
}namespace SecondNamespace {void func() {std::cout << "Inside SecondNamespace" << std::endl;}
}int main() {FirstNamespace::func();  // 调用 FirstNamespace 中的 funcSecondNamespace::func(); // 调用 SecondNamespace 中的 funcreturn 0;
}

在这个示例中,不同命名空间中的同名函数不会冲突,可以通过命名空间限定符来调用。

重点与难点分析

重点

  1. 函数重载的基本概念和实现方法:掌握如何通过定义参数列表不同的同名函数来实现函数重载。
  2. 参数类型转换与重载选择:理解编译器在函数重载中进行的参数类型转换和选择机制。
  3. 默认参数与重载:了解在重载函数中使用默认参数时可能产生的二义性问题。

难点

  1. 参数类型转换的影响:初学者需要通过实践理解参数类型转换如何影响函数重载的选择,避免意外的重载函数调用。
  2. 函数重载的限制:掌握函数重载的规则,避免违反规则导致编译错误。

总结与提高

本节总结

  1. 学习了函数重载的基本概念,掌握了通过定义参数列表不同的同名函数来实现函数重载的方法。
  2. 理解了参数类型转换、默认参数与重载的关系,能够避免重载函数中的二义性问题。
  3. 通过示例代码和练习题,加深了对函数重载的理解和应用。

提高建议

  1. 多练习函数重载的定义与调用:通过编写各种包含重载函数的程序,熟悉重载函数的定义和调用方法。
  2. 深入理解参数类型转换的影响:通过实践掌握参数类型转换在重载

函数选择中的影响,确保调用的是预期的重载函数。
3. 避免重载函数中的二义性问题:在编写重载函数时,合理设计参数列表,避免因默认参数或类型转换导致的二义性问题。

6.5 特殊用途语言特性

6.5.1 默认实参

默认实参允许我们在函数声明中为形参指定一个默认值。如果调用函数时未提供对应的实参,则使用默认值。默认实参使函数调用更加灵活,简化了代码。

示例代码
#include <iostream>void print(int a = 10, int b = 20) {std::cout << "a: " << a << ", b: " << b << std::endl;
}int main() {print();          // 使用默认实参print(30);        // b 使用默认实参print(40, 50);    // 不使用默认实参return 0;
}

在这个示例中,print函数定义了两个默认实参,在调用时可以选择性地提供参数。

6.5.2 constexpr函数

constexpr函数是在编译时求值的函数,用于常量表达式。constexpr函数的返回值和所有形参都必须是字面值类型,且函数体中只能包含单一的return语句或其他常量表达式。constexpr函数的优点在于提高编译时计算效率,减少运行时的计算开销。

示例代码
#include <iostream>constexpr int factorial(int n) {return (n <= 1) ? 1 : (n * factorial(n - 1));
}int main() {constexpr int result = factorial(5);std::cout << "Factorial of 5 is " << result << std::endl;return 0;
}

在这个示例中,factorial函数是一个constexpr函数,在编译时计算5的阶乘。

6.5.3 内联函数

内联函数(inline function)建议编译器将函数体插入到每个函数调用点,而不是进行一次函数调用。这可以减少函数调用的开销,适用于频繁调用的小型函数。使用inline关键字声明内联函数。

示例代码
#include <iostream>inline int add(int a, int b) {return a + b;
}int main() {int sum = add(3, 4);std::cout << "Sum: " << sum << std::endl;return 0;
}

在这个示例中,add函数是一个内联函数,建议编译器将其内联展开。

6.5.4 调试支持

调试支持特性包括assert和调试信息的输出。assert宏用于在调试期间检测程序中的逻辑错误。调试信息的输出可以使用条件编译和预处理宏。

示例代码
#include <iostream>
#include <cassert>void checkPositive(int x) {assert(x > 0 && "Number is not positive");std::cout << x << " is positive." << std::endl;
}int main() {checkPositive(10);// checkPositive(-5);  // 会触发断言失败return 0;
}

在这个示例中,assert宏用于检查x是否为正数,如果条件不满足则会终止程序并输出错误信息。

NDEBUG预处理变量

在定义了NDEBUG预处理变量后,assert宏将被禁用。通常在发布版本中定义NDEBUG以移除调试断言。

示例代码
#include <iostream>
#include <cassert>// #define NDEBUG  // 取消注释以禁用 assertvoid checkPositive(int x) {assert(x > 0 && "Number is not positive");std::cout << x << " is positive." << std::endl;
}int main() {checkPositive(10);// checkPositive(-5);  // 如果定义了 NDEBUG,不会触发断言失败return 0;
}

在这个示例中,如果定义了NDEBUG,则assert宏将被禁用,不会进行检查。

6.5.5 使用递归

递归函数是指在其自身内部调用自己的函数。递归常用于解决那些可以分解为更小的同类子问题的问题。递归需要定义一个基准情况,以避免无限递归。

示例代码
#include <iostream>int fibonacci(int n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
}int main() {int result = fibonacci(10);std::cout << "Fibonacci of 10 is " << result << std::endl;return 0;
}

在这个示例中,fibonacci函数使用递归计算第10个斐波那契数。

尾递归是递归的一种特殊形式,在递归调用是函数体中最后一个操作。尾递归可以被编译器优化为迭代,从而减少调用栈的开销。C++不强制要求尾递归优化,但许多编译器都支持这种优化。

示例代码
#include <iostream>int factorial(int n, int accumulator = 1) {if (n <= 1) return accumulator;return factorial(n - 1, n * accumulator);
}int main() {int result = factorial(5);std::cout << "Factorial of 5 is " << result << std::endl;return 0;
}

在这个示例中,factorial函数使用尾递归计算阶乘,递归调用是函数体中的最后一个操作。

重点与难点分析

重点

  1. 默认实参:理解默认实参的定义和应用场景,掌握默认实参的使用方法。
  2. constexpr函数:掌握constexpr函数的定义和用法,理解其在编译时计算中的应用。
  3. 内联函数:理解内联函数的作用和定义方法,了解其在减少函数调用开销中的应用。
  4. 调试支持:掌握assert宏的用法,理解条件编译和预处理宏在调试信息输出中的应用,特别是NDEBUG预处理变量的使用。
  5. 递归与尾递归优化:理解递归函数和尾递归优化的概念及其应用场景。

难点

  1. 默认实参的二义性问题:初学者需要理解在重载函数中使用默认实参可能产生的二义性问题。
  2. constexpr函数的限制:初学者需要理解constexpr函数的限制条件,确保其符合编译时计算的要求。
  3. 尾递归优化的实现:掌握尾递归优化的实现方法,理解其在减少调用栈开销中的作用。

练习题解析

  1. 练习6.25:编写一个函数,使用默认实参计算两个数的和。
    • 示例代码
#include <iostream>int add(int a, int b = 5) {return a + b;
}int main() {std::cout << add(3) << std::endl;    // 使用默认实参 b = 5std::cout << add(3, 7) << std::endl; // 不使用默认实参return 0;
}
  1. 练习6.26:编写一个constexpr函数,计算一个数的平方。
    • 示例代码
#include <iostream>constexpr int square(int x) {return x * x;
}int main() {constexpr int result = square(5);std::cout << "Square of 5 is " << result << std::endl;return 0;
}
  1. 练习6.27:编写一个内联函数,计算两个数的最小值。
    • 示例代码
#include <iostream>inline int min(int a, int b) {return (a < b) ? a : b;
}int main() {int result = min(3, 7);std::cout << "Min: " << result << std::endl;return 0;
}
  1. 练习6.28:编写一个使用assert宏的函数,检查一个数是否为负数,并使用NDEBUG预处理变量禁用assert
    • 示例代码
#include <iostream>
#include <cassert>// #define NDEBUG  // 取消注释以禁用 assertvoid checkNegative(int x) {assert(x < 0 && "Number is not negative");std::cout << x << " is negative." << std::endl;
}int main() {checkNegative(-10);// checkNegative(5);  // 如果定义了 NDEBUG,不会触发断言失败return 0;
}
  1. 练习6.29:编写一个递归函数,计算一个数的阶乘,并实现尾递归优化。
    • 示例代码
#include <iostream>int factorial(int n) {if (n <= 1) return 1;return n * factorial(n - 1);
}int factorialTail(int n, int accumulator = 1) {if (n <= 1) return accumulator;return factorialTail(n - 1, n * accumulator);
}int main() {int result = factorial(5);std::cout << "Factorial of 5 is " << result << std::endl;int tailResult = factorialTail(5);std::cout << "Tail Factorial of 5 is " << tailResult << std::endl;return 0;
}

总结与提高

本节总结

  1. 学习了默认实参、constexpr函数、内联函数、调试支持(包括assertNDEBUG)、递归和尾递归优化的基本概念和应用。
  2. 掌握了这些特殊用途的语言特性的定义和用法,理解其在提高编译时计算效率、减少运行时开销和调试程序中的作用。
  3. 通过示例代码和练习题,加深了对这些语言特性的理解和应用。

提高建议

  1. 多练习默认实参和内联函数的使用:通过编写各种包含默认实参和内联函数的程序,熟悉其定义和应用场景。
  2. 深入理解constexpr函数和调试支持特性:通过实践掌握constexpr函数和调试支持特性的使用方法,提高编译时计算效率和程序的调试效率。
  3. 掌握递归与尾递归优化:在编写递归函数时,合理设计基准情况和递归步骤,掌握尾递归优化的实现方法,提高递归函数的效率。

6.6 函数匹配

6.6.1 函数匹配概述

函数匹配(Function Matching)是指在函数调用时,根据提供的实参类型和数量选择最合适的重载函数。当编译器遇到多个同名函数时,它会根据函数匹配规则确定哪个函数最适合被调用。如果没有找到合适的匹配,或者存在二义性,编译器将报错。

6.6.2 重载决议

重载决议(Overload Resolution)是编译器选择最匹配的重载函数的过程。编译器通过检查函数的形参列表和调用时提供的实参列表,按照以下规则进行匹配:

  1. 精确匹配:实参类型与形参类型完全相同。
  2. 标准类型转换:如从int转换到double,或从char转换到int
  3. 用户定义的类型转换:如通过构造函数或转换运算符进行的类型转换。
  4. 省略默认实参:当实参数量少于形参数量时,编译器会使用默认实参补足。
示例代码
#include <iostream>void print(int a) {std::cout << "Integer: " << a << std::endl;
}void print(double b) {std::cout << "Double: " << b << std::endl;
}void print(const std::string &c) {std::cout << "String: " << c << std::endl;
}int main() {print(10);           // 精确匹配 print(int)print(3.14);         // 精确匹配 print(double)print("Hello");      // 精确匹配 print(const std::string &)return 0;
}

在这个示例中,编译器根据实参的类型选择最合适的重载函数。

6.6.3 最佳匹配

当存在多个候选函数时,编译器会选择“最佳匹配”的函数。最佳匹配是指实参类型和形参类型之间的转换代价最小的匹配。如果没有唯一的最佳匹配,编译器将报错,指出存在二义性。

示例代码
#include <iostream>void func(int a) {std::cout << "Integer: " << a << std::endl;
}void func(double b) {std::cout << "Double: " << b << std::endl;
}void func(float c) {std::cout << "Float: " << c << std::endl;
}int main() {func(10);      // 调用 func(int)func(3.14);    // 调用 func(double)func(2.5f);    // 调用 func(float)return 0;
}

在这个示例中,编译器选择与实参类型最匹配的函数进行调用。

6.6.4 二义性

如果存在多个同等匹配的重载函数,编译器将无法确定调用哪个函数,从而报二义性错误。为了避免这种情况,重载函数的形参类型应尽量明确。

示例代码
#include <iostream>void ambiguous(int a) {std::cout << "Integer: " << a << std::endl;
}void ambiguous(double b) {std::cout << "Double: " << b << std::endl;
}void ambiguous(long c) {std::cout << "Long: " << c << std::endl;
}int main() {// ambiguous(10L);  // 错误:二义性,long 可匹配 int 和 doublereturn 0;
}

在这个示例中,10L可以匹配intdouble,编译器无法确定调用哪个重载函数,导致二义性错误。

6.6.5 默认实参与重载

使用默认实参时,需要小心避免重载函数之间的二义性问题。默认实参与重载函数结合使用时,应确保每个重载版本都具有唯一的匹配。

示例代码
#include <iostream>void display(int a, int b = 10) {std::cout << "a: " << a << ", b: " << b << std::endl;
}void display(double a) {std::cout << "a: " << a << std::endl;
}int main() {display(5);     // 调用 display(int, int),使用默认参数display(3.14);  // 调用 display(double)return 0;
}

在这个示例中,display(5)调用了display(int, int)并使用默认参数,而display(3.14)调用了display(double)

6.6.6 特殊匹配情况

一些特殊情况可能影响函数匹配,包括使用模板函数和函数指针等。模板函数的匹配规则更加复杂,编译器会根据模板实参推断和匹配规则进行选择。

示例代码
#include <iostream>template <typename T>
void templateFunc(T a) {std::cout << "Template: " << a << std::endl;
}void templateFunc(int a) {std::cout << "Non-template: " << a << std::endl;
}int main() {templateFunc(10);    // 调用非模板函数templateFunc(3.14);  // 调用模板函数return 0;
}

在这个示例中,templateFunc(10)调用了非模板函数,而templateFunc(3.14)调用了模板函数。

重点与难点分析

重点

  1. 函数匹配规则:理解函数匹配的基本规则,包括精确匹配、标准类型转换和用户定义的类型转换。
  2. 最佳匹配:掌握编译器选择最佳匹配的过程,理解如何避免二义性。
  3. 默认实参与重载:了解默认实参与重载结合使用时可能产生的问题及其解决方法。

难点

  1. 二义性问题:初学者需要通过实践理解二义性问题的产生原因及其解决方法,避免重载函数之间的冲突。
  2. 模板函数匹配:掌握模板函数的匹配规则,理解模板实参推断和匹配的复杂性。

总结与提高

本节总结

  1. 学习了函数匹配的基本规则,掌握了重载决议、最佳匹配和二义性问题的解决方法。
  2. 理解了默认实参与重载结合使用时的注意事项,避免二义性问题的产生。
  3. 通过示例代码和练习题,加深了对函数匹配和

模板函数匹配的理解和应用。

提高建议

  1. 多练习函数匹配的定义与调用:通过编写各种包含重载函数的程序,熟悉函数匹配的规则和重载决议的过程。
  2. 深入理解二义性问题:通过实践掌握二义性问题的产生原因及其解决方法,避免重载函数之间的冲突。
  3. 掌握模板函数的匹配规则:在编写模板函数时,合理设计模板参数,理解模板实参推断和匹配的复杂性,提高代码的灵活性和可维护性。

6.7 函数指针

6.7.1 函数指针概述

函数指针是指向函数的指针。通过函数指针,我们可以间接调用函数,并且可以将函数作为参数传递给其他函数。这为编写灵活和可重用的代码提供了便利。

6.7.2 定义和初始化函数指针

函数指针的定义包括返回类型和参数列表,形式如下:

returnType (*pointerName)(parameterList);

要初始化函数指针,可以将其指向一个具有相同签名的函数。

示例代码
#include <iostream>// 函数定义
int add(int a, int b) {return a + b;
}int main() {// 定义函数指针int (*funcPtr)(int, int) = add;// 通过函数指针调用函数int result = funcPtr(3, 4);std::cout << "Result: " << result << std::endl;return 0;
}

在这个示例中,funcPtr是一个函数指针,指向add函数,通过该指针调用add函数并获取结果。

6.7.3 函数指针作为参数

函数指针可以作为参数传递给其他函数,从而实现对不同函数的灵活调用。

示例代码
#include <iostream>// 函数定义
int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}// 接受函数指针作为参数的函数
int compute(int (*func)(int, int), int x, int y) {return func(x, y);
}int main() {// 调用 compute 函数,传递 add 和 subtract 函数指针int sum = compute(add, 5, 3);int difference = compute(subtract, 5, 3);std::cout << "Sum: " << sum << std::endl;std::cout << "Difference: " << difference << std::endl;return 0;
}

在这个示例中,compute函数接受一个函数指针作为参数,并调用传递的函数。

6.7.4 函数指针作为返回值

函数指针也可以作为函数的返回值,从而实现对不同函数的动态选择。

示例代码
#include <iostream>// 函数定义
int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}// 返回函数指针的函数
int (*getOperation(char op))(int, int) {if (op == '+') {return add;} else {return subtract;}
}int main() {// 获取函数指针并调用函数int (*operation)(int, int) = getOperation('+');int result = operation(10, 5);std::cout << "Result: " << result << std::endl;return 0;
}

在这个示例中,getOperation函数根据输入字符返回相应的函数指针,通过该指针调用相应的函数。

6.7.5 使用typedef简化函数指针

使用typedef可以简化函数指针的定义和使用,使代码更加可读和易于维护。

示例代码
#include <iostream>// 使用 typedef 定义函数指针类型
typedef int (*operationFunc)(int, int);// 函数定义
int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}// 接受函数指针作为参数的函数
int compute(operationFunc func, int x, int y) {return func(x, y);
}int main() {// 使用 typedef 定义的函数指针类型operationFunc op = add;int result = compute(op, 7, 3);std::cout << "Result: " << result << std::endl;return 0;
}

6.7.6 使用using简化函数指针

using关键字是C++11引入的一种新方式,用于定义类型别名。相对于typedefusing语法更加直观和易于理解,特别是在处理复杂类型时。

示例代码
#include <iostream>// 使用 using 定义函数指针类型
using operationFunc = int(*)(int, int);// 函数定义
int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}// 接受函数指针作为参数的函数
int compute(operationFunc func, int x, int y) {return func(x, y);
}int main() {// 使用 using 定义的函数指针类型operationFunc op = add;int result = compute(op, 7, 3);std::cout << "Result: " << result << std::endl;return 0;
}

在这个示例中,使用using定义了operationFunc类型,使函数指针的定义和使用更加简洁。

6.7.7 指向成员函数的指针

指向成员函数的指针不同于普通函数指针。定义指向成员函数的指针时,需要指定类的作用域。

示例代码
#include <iostream>class Calculator {
public:int add(int a, int b) const {return a + b;}int subtract(int a, int b) const {return a - b;}
};int main() {Calculator calc;// 定义指向成员函数的指针int (Calculator::*funcPtr)(int, int) const = &Calculator::add;// 通过指针调用成员函数int result = (calc.*funcPtr)(10, 5);std::cout << "Result: " << result << std::endl;return 0;
}

在这个示例中,funcPtr是一个指向Calculator类成员函数的指针,通过该指针调用add函数。

重点与难点分析

重点

  1. 函数指针的定义和初始化:掌握函数指针的定义方法,并能够初始化和调用函数指针。
  2. 函数指针作为参数和返回值:理解如何将函数指针作为参数传递和返回,提高代码的灵活性。
  3. 使用typedefusing简化函数指针:掌握使用typedefusing简化函数指针定义的方法,提高代码的可读性。
  4. 指向成员函数的指针:理解指向成员函数的指针的定义和使用,掌握其与普通函数指针的区别。

难点

  1. 函数指针的语法:初学者需要通过实践掌握函数指针的语法,避免常见的语法错误。
  2. 指向成员函数的指针:掌握指向成员函数的指针的定义和使用方法,理解其与普通函数指针的不同。

总结与提高

本节总结

  1. 学习了函数指针的定义和初始化方法,掌握了如何通过函数指针调用函数。
  2. 理解了函数指针作为参数和返回值的应用,能够编写灵活和可重用的代码。
  3. 掌握了使用typedefusing简化函数指针定义的方法,提高代码的可读性。
  4. 理解了指向成员函数的指针的定义和使用,能够通过该指针调用成员函数。

提高建议

  1. 多练习函数指针的定义与调用:通过编写各种包含函数指针的程序,熟悉函数指针的语法和应用场景。
  2. 深入理解指向成员函数的指针:通过实践掌握指向成员函数的指针的定义和使用方法,理解其与普通函数指针的不同。
  3. 使用typedefusing提高代码可读性:在编写复杂函数指针时,使用typedefusing简化定义,提高代码的可读性和可维护性。

本主页会定期更新,为了能够及时获得更新,敬请关注我:点击左下角的关注。也可以关注公众号:请在微信上搜索公众号“iShare爱分享”并关注,或者扫描以下公众号二维码关注,以便在内容更新时直接向您推送。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/30817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最新OPPO 真我手机 一加手机 使用adb命令永久关闭系统更新教程

使用adb命令永久关闭系统更新 一、先了解手机系统二、Android 11 以下使用adb 命令永久关闭系统更新1、adb 官方下载2、小白开启 USB 调试模式教程&#xff08;熟手跳过&#xff09;三、Android 12 以上使用adb 命令永久关闭系统更新什么您还是不会弄&#xff01;赞赏我&#x…

MYSQL 四、mysql进阶 3(存储引擎)

mysql中表使用了不同的存储引擎也就决定了我们底层文件系统中文件的相关物理结构。 为了管理方便&#xff0c;人们把连接管理、语法解析、查询优化这些并不涉及真实数据存储的功能划分为 Mysql Server的功能&#xff0c;把真实存取数据的功能划分为存储引擎的功能&…

systemd的实现原理

systemd是现代Linux系统中的初始化系统和服务器管理器&#xff0c;而systemctl是用于与systemd交互的命令行工具。 systemd是一个守护进程&#xff0c;systemctl是命令行管理工具&#xff1a;systemd是用于管理Linux系统的初始化过程和后台服务的初始化系统&#xff0c;而syst…

Windows10 + fydeOS双系统!简单几步完成

前言 最近发现小伙伴对于fydeOS热情是真的不减&#xff0c;啧啧啧……今天闲来无事&#xff0c;就来讲讲双系统Windows10 fydeOS的安装方法吧&#xff01; Windows10 FydeOS双系统安装过程其实很简单&#xff0c;不过要建议先安装好Windows10系统。 虽然先安装好fydeOS之后…

SpringBootWeb 篇-入门了解 Vue 前端工程的创建与基本使用

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 基于脚手架创建前端工程 1.1 基于 Vue 开发前端项目的环境要求 1.2 前端工程创建的方式 1.2.1 基于命令的方式来创建前端工程 1.2.2 使用图形化来创建前端工程 1.…

如何建立私域流量?私域流量怎么运营,一文读懂

当全网都在讨论私域流量&#xff0c;你是不是也有很多问号呢&#xff1f; 互联网高速发达&#xff0c;消费形式日新月异&#xff0c;跟不上时代就会被时代淘汰&#xff0c;接下来&#xff0c;我们就从3个层面深度讨论下私域流量究竟是什么&#xff1f;为什么要玩转私域流量&am…

【保姆级教程】Linux 基于 Docker 部署 MySQL 和 Nacos 并配置两者连接

一、Linux 部署 Docker 1.1 卸载旧版本&#xff08;如有&#xff09; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine1.2 安装 yum-utils 包 sudo yum install -y…

微信多开器

由于微信的限制&#xff0c;我们平时只能登录一个微信&#xff0c;要登录多个微信一般需要多台手机&#xff0c;很显然这种方法很费手机&#xff01;&#xff01;一个微信多开神器可以给你省下好几台手机钱&#xff0c;抓紧拉下来放手机里落灰http://www.xbydon.online/?p132 …

NetSuite 审批工作流与事务处理类型的限制关系

在最近的实践中&#xff0c;用户提出可否对Credit Memo与Vendor Prepayment Application两种事务处理类型进行审批参与&#xff0c;当提出来的时候我们并没有直接在系统中进行测试&#xff0c;而是以常规事务处理的角度认为可以满足客户的需求&#xff1b; 但在沙盒环境中讨论…

RocketMQ快速入门:如何保证消息不丢失|保证消息可靠性(九)

0. 引言 在金融、电商等对数据完整性要求极高的行业&#xff0c;消息的丢失可能会导致数据不一致&#xff0c;严重影响业务逻辑和数据统计&#xff0c;也影响客户体验&#xff0c;所以在很多业务场景下&#xff0c;我们都要求数据不能丢失。而rocketmq中&#xff0c;如何对消息…

当游戏遭遇安全问题,我们应该怎么做?

在游戏安全领域&#xff0c;专业性最差、但最常见的案例类型是DDoS攻击&#xff08;分布式拒绝服务攻击&#xff09;。出于它的特性&#xff0c;中小厂商、独立开发者较容易遭受这类攻击。 例如&#xff0c;今年2月29日上线的手游《雷索纳斯》就遭受了名为ACCN组织发起的DDoS攻…

【内含优惠码】重磅发售!《2023年度中国量化投资白皮书》(纸质版)

这是可以公开了解量化行业图景的&#xff0c;为数不多资料。 简介 《2023年度中国量化投资白皮书》由宽邦科技、华泰证券、金融阶、华锐技术、AMD、阿里云、英迈中国等多家机构联合发起编写&#xff0c;并于2024年6月15日正式发布&#xff0c;全书公17万字6大章节勾勒最新量化…

Studying-代码随想录训练营day15| 222.完全二叉树的节点个数、110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和

第十五天&#xff0c;二叉树part03&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 257.完全二叉树的节点个数 110.平衡二叉树 257.二叉树的所有路径 404.左叶子之和 总结 257.完全二叉树的节点个数 文档讲解&#xff1a;代码随想录完全二叉树的节点个数 视频讲解…

Arduino平台软硬件原理及使用——无源蜂鸣器模块的使用

文章目录 一、蜂鸣器发声原理 二、无源蜂鸣器与有源蜂鸣器的区分 三、无源蜂鸣器模块在Arduino中的使用 一、蜂鸣器发声原理 上图为常见的不同封装及规格的蜂鸣器。 同蜜蜂、知了等昆虫发声原理一样&#xff0c;蜂鸣器同样靠振动来发出声音&#xff1b; 如上图为无源蜂鸣器的内…

【总结】ui自动化selenium知识点总结

1. 大致原理 首页安装第三方库selenium库&#xff0c; 其次要下载好浏览器驱动文件&#xff0c;比如谷歌的 chromedriver.exe&#xff0c;配置上环境变量。 使用selenium的webdriver类去创建一个浏览器驱动对象赋值叫driver&#xff0c;一个浏览器驱动对象就可以 实现 对浏…

【vue3|第11期】Vue3中的ref属性:让元素引用变得简单

日期&#xff1a;2024年6月19日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

招聘主播?小心是大陷阱!!!

高薪招聘主播的骗局通常涉及一系列精心设计的步骤&#xff0c;旨在引诱求职者上钩并从中获利。以下是这种骗局常见的几个关键环节&#xff1a; 首先&#xff0c;骗子会通过各种渠道发布诱人的招聘信息&#xff0c;声称正在寻找有潜力的主播&#xff0c;并承诺提供高额的底薪和…

虚拟3D沉浸式展会编辑平台降低了线上办展的门槛

在数字化浪潮的引领下&#xff0c;VR虚拟网上展会正逐渐成为企业展示品牌实力、吸引潜在客户的首选平台。我们与广交会携手走过三年多的时光&#xff0c;凭借优质的服务和丰富的经验&#xff0c;赢得了客户的广泛赞誉。 面对传统展会活动繁多、企业运营繁忙的挑战&#xff0c;许…

【绝对有用】刚刚开通的GPT-4o计算这种数学题目出现问题了

欢迎关注如何解决以上问题的方法&#xff1a;查看个人简介中的链接的具体解决方案

[Qt的学习日常]--窗口

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、窗口的分…