概述
C++ 是 C 的超集。
C++ 诞生于贝尔实验室,开发者是 Bjarne Stroustrup (本贾尼·斯特劳斯特卢普)。
C++ 融合了三种不同的编程方式
- C 语言代表的过程性语言
- 面向对象 (OOP, Object Oriented Programming)
- 泛型编程
面向过程
- 以过程为中心的编程思想
- 功能分解
- 自顶向下
- 逐层细化
- 缺点
- 不符合人的思维习惯
- 重用性低,维护困难
面向对象 (OOP) 的三大特性
面向对象中,算法和数据结构被看作一个整体,称作对象。
使用代码模块,增加了代码的重用率,便于开发、维护。
- 封装
- 把客观的事物封装成抽象的类
- 继承
- 使得对象可以继承另一类对象的特征和能力
- 避免公用代码的重复开发,减少代码和数据冗余
- 多态
- 一个接口,多种方法
可移植性和标准
- 美国国家标准局 (American National Standards Institute, ANSI) 在1998年制定出第一套 C++ 标准,叫做 C++98
- 2011 年又新增了 C++11
- 2020年又新增了 C++20
第一个程序:hello world
“`c++
#include <iostream> // 标准输入输出流,相当于 <stdio.h>
using namespace std; // 使用标准命名空间
// 第一个 C++ 程序
void test01()
{
// cout:标准输出流对象
// <<:在 cout 后拼接输出的内容
// endl:end line,刷新缓冲区,并且换行
cout << "hello world" << endl; // hello world
}
“`
C++ 对 C 的扩展
作用域控制增强
- 双冒号作用域运算符 (::)
- 代表作用域
-
如果前面什么都不添加,代表全局作用域
-
同名的变量,默认局部变量优先级更高
-
示例
“`c++
int atk = 1000;void test02()
{
int atk = 2000;
cout << "atk = " << atk << endl; // 2000
::cout << "atk = " << ::atk << ::endl; // 1000
std::cout << "atk = " << ::atk << std::endl; // 1000
}“`
-
namespace (命名空间)
主要用途是解决名称冲突。命名空间下可以存放:变量、函数、结构体、类。
- 命名空间必须要声明在全局作用域,不可以命名到局部作用域
-
命名空间可以嵌套命名空间
-
命名空间是开放的,可以随时给命名空间添加新的成员
-
命名空间可以是匿名的
-
命名空间可以起别名
-
示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
// 主要用途是,解决名称冲突
namespace A
{
int atk = 10;
}// 命名空间下可以存放: 变量、函数、结构体、类
namespace B
{
int atk = 20;
void func(); // 声明和实现分离
struct Person
{};
class Animal
{};
}void B::func() // 函数实现
{}// 命名空间可以嵌套命名空间
namespace C
{
int a = 10;
namespace D
{
int d = 20;
}
}// 命名空间是开放的,可以随时给命名空间添加新的成员
namespace C
{
int b = 20;
}// 命名空间可以是匿名的
namespace
{
int cc = 10; // 匿名空间中,相当于 static int cc = 10;
int dd = 20;
}//
void test01()
{
cout << A::atk << endl; // 10
cout << B::atk << endl; // 20<pre><code>// 命名空间必须要声明在全局作用域
//namespace C {}; // Error! 不可以命名到局部作用域cout << C::a << endl; // 10
cout << C::b << endl; // 20cout << ::cc << endl; // 10
cout << dd << endl; // 20// 命名空间可以起别名
namespace aliasname = C;
cout << aliasname::b << endl; // 20
</code></pre>}
int main(void)
{
test01();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
-
using 声明和编译指令
- using 声明
- 语法
“`c++
using 命名空间名称::元素
“` -
当 using 声明与就近原则同时出现,会出错,要尽量避免
- 语法
-
using 编译指令
- 当 using 的编译指令与就近原则同时出现,优先使用就近原则
- 当 using 编译指令有多个,需要加作用域区分
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
namespace A
{
int a = 10;
}namespace B
{
int a = 111;
}void test01()
{
int a = 20;<pre><code>// 1. 当 using 声明与就近原则同时出现,会出错,要尽量避免
//using A::a; // Error!
cout << a << endl; //// 2. 当 using 的编译指令与就近原则同时出现,优先使用就近原则
using namespace A;
cout << a << endl; // 20// 3. 当 using 编译指令有多个,需要加作用域区分
cout << A::a << endl; // 10
cout << B::a << endl; // 111
</code></pre>}
int main(void)
{
test01();<pre><code>system("pause");
return 0;
</code></pre>}
“`
- using 声明
检测增强
-
全局变量检测增强
“`c++
int a;
int a = 10; // 重定义
“`- C++ 会检测出重定义
- 函数的检测增强
- 会检查所有返回值、形参类型、函数调用的参数个数
-
对比
- C 语言中,int func() 表示返回值是 int,接受任意参数;int func(void) 表示返回值为 int 的无参函数
- C++ 中,int func() 和 int func(void) 具有相同的意义,都表示返回值为 int 的无参函数
- 示例
“`c++
getRet(w, h)
{
return w * h;
}void test01()
{
printf("%d\n", getRet(10, 20, 30)); // 200
}“`
-
类型转换检测增强
-
C++ 中,不同类型的变量一般不能直接赋值,需要相应的强转
-
示例
“`c++
void test02()
{
//char* p = malloc(64); // 报错
char* p = (char *)malloc(64);
}
“`
类型增强
-
struct 类型增强
- C 语言下的结构体中不允许有函数,C++ 可以在结构体中放函数
-
创建结构体变量,可以省略关键字 struct
-
示例
“`c++
struct Person
{
int age;
void func()
{
age++;
}
};void test03()
{
Person p; // 创建结构体变量,可以省略关键字 struct
p.age = 100;
}“`
-
新增 bool 类型关键字
- C 语言没有这个数据类型,C++ 有这个数据类型,默认为 0
- C99 标准中也有 bool 类型,头文件 stdbool.h
- bool 类型只有两个值:true (整数 1) 和 false (整数 0)
- 非 0 值会自动都转成 1
- 只占 1 个字节
- sizeof(bool) = 1
- 示例
“`c++
bool flag;void test04()
{
cout << sizeof(bool) << endl; // 1
cout << flag < b ? a : b;
printf("%d\n", ret1); // 2int ret2 = a > b ? a : b = 100; // C 语言下返回的是值;C++ 返回的是变量
printf("%d\n", ret1); // 2printf("%d, %d\n", a, b); // 1, 100
}“`
- C 语言没有这个数据类型,C++ 有这个数据类型,默认为 0
-
C++ 返回的是变量本身 (引用),为左值,可以赋值
“`c++
// 三目运算符增强
void test05()
{
int a = 1;
int b = 2;<pre><code>int ret1 = a > b ? a : b;
printf("%d\n", ret1); // 2//int ret2 = a > b ? a : b = 100; // C 语言下返回的是值;C++ 返回的是变量
int ret3 = *(a > b ? &a : &b) = 100;
printf("%d\n", ret1); // 2printf("%d, %d\n", a, b); // 1, 100
</code></pre>}
“`
-
左值和右值
- 可以放在赋值操作符左边的是左值,可以放到赋值操作符右边的是右值
- 左值为 Lvalue,L 代表 Location,表示内存可以寻址,可以赋值
- 右值为 Rvalue,R 代表 Read,就是可以知道它的值
- 例如:int temp = 10
- temp 在内存中有地址,10 没有,但是可以 Read 到它的值
const 增强
- 是否为 const 分配内存
- C 语言中的 const
- 被认为是一个全局只读变量,会给 const 分配内存
-
示例
const int arrSize = 10; int arr[arrSize];
Note: 上面代码看似合理,但是是错误的。因为 arrSize 占用某块内存,所以 C 编译器不知道它在编译时的值是多少。
-
C++ 中的 const
- 一个 const 不必创建内存空间
- 是否为 const 常量分配内存空间依赖于如何使用
- C 语言中的 const
- 全局和局部 const
- C 语言下
- 全局 const
- 直接修改,失败;间接修改,语法通过,运行失败
- 被存储到只读数据段,所以不可修改
- 局部 const
- 直接修改,失败;间接修改,成功
- 存储在堆栈区,不能通过变量直接修改,但是可以跳过编译器的检查,通过指针间接修改
- 示例
“`c++
int m = 10;
//const int m = 10;void test06()
{
//m = 20;
int* p = &m;
*p = 20;<pre><code>printf("%d\n", m); // 20
const int mb = 11;
//mb = 22;
int* mp = &mb;
*mp = 22;printf("%d\n", mb); // 22
</code></pre>}
“`
- 全局 const
-
C++ 下
const 修饰的变量,称为常量,可以初始化数组。
- 全局 const
- 和 C 语言结论一样
- 当声明 extern 或对变量取地址时,编译器会分配存储地址,变量存储在只读数据段,所以不可修改
- 局部 const
- 直接修改,失败;间接修改,失败
- 使用了符号表,利用了临时指针,不是原指针
- 要区别对待
- 对于基础数据类型,类似 const int a = 10 这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
- 分配内存的情况
- 对 const 变量取地址,会分配临时内存
-
使用普通变量,初始化 const 变量
“`c++
<h1>include</h1>
“`
-
对于自定义数据类型,会分配内存
- 分配内存的情况
-
对于基础数据类型,如果用一个变量初始化 const 变量,类似 const int a = b,那么也会给 a 分配内存
-
对于自定义数据类型,例如,类,也会分配内存
- 对于基础数据类型,类似 const int a = 10 这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
- 直接修改,失败;间接修改,失败
-
示例
“`c++
int m = 10;
//const int m = 10;void test06()
{
//m = 20;
int* p = &m;
*p = 20;<pre><code>printf("%d\n", m); // 20
const int mb = 11;
//mb = 22;
int* mp = (int*)&mb; // 报错
*mp = 22;printf("%d\n", mb); // 11
</code></pre>}
“`
- 全局 const
- C 语言下
-
链接属性
- C 语言下
- const 修饰的全局变量默认是外部链接属性
- C 文件中,如果两个文件中都有 const int a,会报重定义错误,而 C++ 不会报错
- C++ 下
- const 修饰的全局变量默认是内部链接属性
- 可以使用 extern 提高作用域,变为外部链接
“`c++
extern const int g_a = 100;
“`
- 可以使用 extern 提高作用域,变为外部链接
- const 修饰的全局变量默认是内部链接属性
- C 语言下
-
尽量用 const 代替 #define
- 宏是不被编译的,因为在预处理阶段已经被替换了
-
define 出的宏常量,没有数据类型,不重视作用域
“`c++
//#define MAX 1024
//#udefine MAX
const int max = 1024; // 用常量代替宏
“`- 编译报错时,可能提到了 1024,但是没有提到 MAX,所以建议用常量代替宏
- const 和 define 区别
- const 有类型,可以进行编译器安全检查;define 无类型,不进行类型检查
- const 有作用域,而 define 不重视作用域
- 能否用变量定义数组
int a = 10; int arr[a];
- VS 中不可以
- Linux GCC 下,C99 可以
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
<h1>include</h1>
using namespace std;
// 对 const 变量取地址,会分配临时内存
void test01()
{
const int a = 10;
int* p = (int*)&a;
}// 使用普通变量,初始化 const 变量
void test02()
{
int a = 10;
const int b = a;<pre><code>int* p = (int *)&b;
*p = 100;cout << a << endl; // 10
cout << b << endl; // 100
cout << *p <age = 10;cout << "name: " << p.name << ", age: " << p.age << endl;
</code></pre>}
int main(void)
{
//test01();
//test02();
test03();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
C++ 新增
引用 (reference)
-
基本语法
- 语法
“`c++
类型 (与原名的类型必须一致) &别名 = 原名
“`- 引用的目的,起别名
- &:在此不是求地址运算,而是起标识作用
- 必须在声明引用变量时进行初始化
- 引用初始化之后不能改变
- 不能有 NULL 引用。必须确保引用是和一块合法的存储单元关联
- 可以建立对数组的引用
- 注意事项
- 引用必须初始化
- 引用一旦初始化后,就不可以引向其他变量
- 对数组建立引用
- 直接建立引用
- 先定义出数组类型,再通过类型定义引用
- 引用的本质
- 在 C++ 内部实现是一个指针常量
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
// 引用的基本语法
void test01()
{
int a = 10;
int& b = a; // 引用
b = 100;<pre><code>cout << "a = " << a << ", b = " << b << endl; // a = 100, b = 100
</code></pre>}
// 注意事项
void test02()
{
int a = 10;
//int& b; // 引用必须初始化
int& b = a;<pre><code>int c = 20;
//&b = c;
b = c; // 引用一旦初始化后,就不可以引向其他变量cout << "a = " << a;
cout << ", b = " << b;
cout << ", c = " << c << endl; // a = 20, b = 20, c = 20
</code></pre>}
// 对数组建立引用
void test03()
{
int arr[10];
int(&pArr1)[10] = arr; // 直接建立引用<pre><code>typedef int(ARRAY_TYPE)[10]; // 先定义出数组类型,再通过类型定义引用
ARRAY_TYPE& pArr2 = arr;for (int i = 0; i < 10; i++)
{
arr[i] = i + 10;
cout << pArr1[i] << " " << pArr2[i] << endl;
}
</code></pre>}
// 引用的本质是一个指针常量
void testFunc(int& ref) //发现是引用,转换为 int* const ref = &a;
{
ref = 100; // ref 是引用,转换为 *ref = 100
}void test04()
{
int a = 10;
int& aRef = a; // 自动转换为 int* const aRef = &a; 这也能说明引用为什么必须初始化
aRef = 20; // 内部发现 aRef 是引用,自动转换为: *aRef = 20;
cout << "a = " << a << ", aRef = " << aRef << endl; // a = 20, aRef = 20<pre><code>testFunc(a);
</code></pre>}
int main(void)
{
test01();
test02();
test03();
test04();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
- 语法
-
函数中的引用
- 参数的传递方式
- 值传递
-
地址传递
-
引用传递 (C++)
- 函数调用时,传递的参数不必加 & 符
- 在被调函数中,不必在参数前加 * 符
- C++ 主张用引用传递取代地址传递方式
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
// 1.值传递
void swap1(int a, int b) // 交换数值
{
int temp = a;
a = b;
b = temp;
}// 2.地址传递
void swap2(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}// 3.引用传递
void swap3(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}int main(void)
{
int a = 10;
int b = 20;<pre><code>swap1(10, 20); // a = 20, b = 10
cout << "a = " << a << ", b = " << b << endl; // a = 10, b = 20swap2(&a, &b);
cout << "a = " << a << ", b = " << b << endl; // a = 20, b = 10swap3(a, b);
cout << "a = " << a << ", b = " << b << endl; // a = 10, b = 20system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
-
返回值引用的注意事项
- 引用必须引一块合法的内存空间
- 示意图:int& a = 10;
- 示意图:int& a = 10;
- 不要返回局部变量的引用
-
当函数返回值是引用时,那么函数的调用可以作为左值进行运算
-
示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
int& func1()
{
int a = 10;
return a;
}int& func2()
{
static int a = 10;
return a;
}void test01()
{
// 引用必须引一块合法的内存空间
//int& a = 10; // Error!<pre><code>// 不要返回局部变量的引用
int& ref1 = func1();
cout << "ref1 = " << ref1 << endl; // ref1 = 10
cout << "ref1 = " << ref1 << endl; // ref1 = 2026084744int& ref2 = func2();
cout << "ref2 = " << ref2 << endl; // ref2 = 10
cout << "ref2 = " << ref2 << endl; // ref2 = 10
cout << "ref2 = " << ref2 << endl; // ref2 = 10// 当函数返回值是引用时,那么函数的调用可以作为左值进行运算
func2() = 1000;
cout << "ref2 = " << ref2 << endl; // ref2 = 1000
</code></pre>}
int main(void)
{
test01();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
- 引用必须引一块合法的内存空间
- 参数的传递方式
-
指针的引用
- 利用引用简化指针
-
可以直接用同级指针的引用,给同级指针分配空间
-
示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
struct Person
{
int age;
};// C 语言的方法
void allocateSpace1(Person **p)
{
//struct Person* p = (struct Person<em>)malloc(sizeof(struct Person));
*p = (Person</em>)malloc(sizeof(Person));
(*p)->age = 10;
}// 利用引用简化指针
void allocateSpace2(Person* &pp) // 可以直接用同级指针的引用,给同级指针分配空间
{
pp = (Person*)malloc(sizeof(Person));
pp->age = 20;
}void test01()
{
Person* p = NULL;<pre><code>allocateSpace1(&p);
cout << "age = " <age << endl; // age = 10allocateSpace2(p); // 可以直接用同级指针的引用,给同级指针分配空间
cout << "age = " <age << endl; // age = 20
</code></pre>}
int main(void)
{
test01();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
-
常量的引用
- 语法
“`c++
const 类型 &别名 = 常量值
“`- 字面量不能赋值给引用,但是可以赋给 const 引用
- const 修饰的引用,不能修改
- 常量引用的使用场景
- 常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数
- 将函数的形参定义为常量引用的好处
- 引用不产生新的变量,减少形参与实参传递时的开销
- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用 (防止误操作)
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
void test01()
{
//int& ref = 10; // 内存地址不合法<pre><code>// 加了 const 之后,相当于写成 int temp = 10; const int &ref = temp;
const int& ref = 10;
cout << "ref = " << ref << endl; // ref = 10int* p = (int *)&ref;
*p = 20;
cout << "ref = " << ref << endl; // ref = 20
</code></pre>}
// const 修饰函数中的形参,防止误操作
void showValue(const int& a)
{
//a = 100; // Error! 不允许修改
cout << "a = " << a << endl;
}void test02()
{
int a = 200;<pre><code>showValue(a);
cout << "a = " << a << endl; // a = 200
</code></pre>}
int main(void)
{
test01();
test02();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
- 语法
类
-
组成
- 成员变量
- 成员函数
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
<h1>include</h1>
using namespace std;
const double PI = 3.14;
// 1.设计一个类,求圆的周长
class Circumference
{
public: // 公共权限
double radius; // 半径<pre><code>// 获取周长
double getCircumference()
{
return 2 * PI * radius;
}// 获取半径
double getRadius()
{
return radius;
}// 设置半径
void setRadius(double m_R)
{
radius = m_R;
}
</code></pre>};
void test01()
{
// 实例化一个对象
Circumference c1;<pre><code>// 设置半径
c1.setRadius(10);cout << "半径 = " << c1.getRadius() << endl;
cout << "周长 = " << c1.getCircumference() << endl;
</code></pre>}
// 2.设计一个学生类,属性有姓名、学号,可以给姓名和学号赋值并显示
class Student
{
public:
string name;
string id;<pre><code>// 设置姓名、学号
void setStudent(string n, string i)
{
name = n;
id = i;
}// 显示姓名、学号
void showInfo()
{
cout << "name = " << name << endl;
cout << "id = " << id << endl;
}
</code></pre>};
void test02()
{
Student s1;
s1.setStudent("aaa", "001");
s1.showInfo();<pre><code>Student s2;
s2.setStudent("bbb", "002");
s2.showInfo();
</code></pre>}
int main(void)
{
test01();
test02();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
内联函数
-
宏函数缺陷
- 必须要加括号
- 即使加了括号,有些运算依然与预期不符
- 预定义宏函数没有作用域概念,无法作为一个类的成员函数,也就是说预定义宏没有办法表示类的范围
-
示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
// 宏函数缺陷1:必须加括号保证运算完整性
//#define add(x, y) x + y // 70<h1>define add(x, y) ((x) + (y)) // 90</h1>
// 宏函数缺陷2:即使加了括号,有些运算依然与预期不符
<h1>define compare(a, b) (((a)<(b)) ? (a) : (b))</h1>
// 内联函数
// 函数的声明和实现必须同时加 inline 才算内联函数
// 解决宏缺陷,本身是一个函数,带来宏优势,以空间换时间,在适当的时候展开
inline void func();
inline void func() {}void test01()
{
printf("(10+20)<em>3 = %d\n", add(10, 20) * 3); // (10+20)</em>3 = 90<pre><code>int a = 10;
int b = 20;
cout << compare(++a, b) << endl; // 预期 11,实际 12
</code></pre>}
int main(void)
{
test01();<pre><code>system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
- 必须要加括号
-
概念
- C++ 使用内联函数代替宏函数
- 关键字:inline
- 内联函数是一个真正的函数,具有普通函数的所有行为
- 与普通函数的区别是,内联函数会在适当的地方像宏一样展开,所以不需要函数调用的开销
- 函数的声明和实现必须同时加 inline 才算内联函数
- 在类内部的成员函数前,都隐式加了关键字 inline,也就是说,类内部的成员函数自动成为内联函数
- C++ 使用内联函数代替宏函数
-
优点
- 解决宏缺陷,本身是一个函数,带来宏优势,以空间换时间 (内联函数占用空间,但是省去了函数调用时候的压栈、跳转、返回的开销),在适当的时候展开
- 内联处理的例外情况
某些特殊情况下,写了关键字 inline 也不会按照内联方式处理。
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 不能对函数进行取地址操作
- 注意事项
- 内联函数只是给编译器一个建议,编译器不一定会接受这种建议
- 有时候编译器会自动将普通函数做内联编译
函数的默认参数和占位参数
- 默认参数
- 在形参中指定默认值
- 如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
- 函数的声明和实现,只能有一个提供默认参数,不可以同时添加默认参数
- 占位参数
- 只写一个类型进行占位,调用时必须传入占位值
- 占位参数也可以有默认值
- 示例
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
using namespace std;
// 1.默认参数 -> 在形参中指定默认值
// 如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
int func1(int a, int b = 20, int c = 30)
{
return a + b + c;
}// 函数的声明和实现,只能有一个提供默认参数,不可以同时添加默认参数
void func2(int a = 10, int b = 20);
//void func2(int a = 10, int b = 20) {} // Error! 重定义默认参数
void func2(int a, int b){}// 2.占位参数
void func3(int a, int) // 函数内部无法使用占位参数
{}void func4(int a, int b, int = 30) // 占位参数也可以设置默认值
{}
int main(void)
{
cout << func1(10) << endl; // 60
func2();<pre><code>// 带占位参数的函数调用
//func3(10); // Error! 错误调用,占位参数也是参数,必须传参
func3(10, 20);
func4(10, 20);system("pause");
return EXIT_SUCCESS;
</code></pre>}
“`
函数重载
-
条件
- 在同一个作用域,函数名称相同,但是函数参数的个数、类型、顺序不同
- 返回值不可以作为函数重载的条件
- 因为我们调用的时候可能会忽略返回值,例如:
func();
,而不是int a = func();
- 因为我们调用的时候可能会忽略返回值,例如:
- 注意事项
- 函数重载中,引用两个版本
- 避免二义性
- 加 const 和不加 const 的引用可以作为
- 函数重载碰到默认参数,也要避免二义性
- 函数重载中,引用两个版本
-
函数重载实现原理
- 原理
- 在 C++ 中有函数重载,会修饰函数名 (这个修饰没有统一标准),如果根据名称修饰方式来查找这个函数,那么就会发生链接错误,导致调用 C 语言函数失败
- 报错
- 一个无法解析的外部命令
- 解决方法
- 在 C++ 代码中添加代码 extern “C”
“`c++
<h1>define _CRT_SECURE_NO_WARNINGS</h1>
<h1>include</h1>
<h1>include "test.h"</h1>
using namespace std;
//extern "C" void test(); // 已废弃
“`
- 告诉编译器,show 函数用 C 语言的方式
- 很少用,太麻烦
- 这种方法可能已经废弃了,会报错
- 链接规范与 “test” 的早期规范冲突
- 在 C 语言的头文件中添加代码 extern “C”
“`c++
<h1>ifdef __cplusplus</h1>
extern "C" {
<h1>endif</h1>
void test(); // 中间放函数声明
<h1>ifdef __cplusplus</h1>
}
<h1>endif</h1>
“`
- 在 C++ 代码中添加代码 extern “C”
- 原理
-
代码