函数基础
函数的定义与使用
在每一新建的项目中,第一个接触的函数始于 main 函数,他是 C++ 的主函数。
- 函数的定义:
类型说明符 函数名([形式参数列表])
{
函数体;
}
//形式参数列表:数据类型1 变量名1,数据类型2 变量名2,数据类型3 变量名3,....,数据类型n 变量名n
//[]表示:可有可无
- 函数的返回值和返回值类型:函数可以有一个返回值,函数的返回值是需要返回给主调函数的处理结果。
- 语法:
return 表达式; - 除了指定函数的返回值外,return 语句华友一个作用,就是结束当前函数的执行。
- 语法:
- 函数的调用:
- 注意:在定义了一个函数之后,可以直接调用这个函数,但如果希望在定义一个函数前调用它,则需要在调用函数之前添加该函数的函数原型声明。语法格式:
类型说明符 函数名(含类型说明的形参列表); - 声明了函数原型之后,便可以调用其函数。语法:
函数名(实参列表);
- 注意:在定义了一个函数之后,可以直接调用这个函数,但如果希望在定义一个函数前调用它,则需要在调用函数之前添加该函数的函数原型声明。语法格式:
//声明函数原型
//...
//定义在main函数后面的,需要在main函数前面进行原型声明
void init();
void main()
{
//函数体
//...
init();
}
void init()
{
//函数体
//...
}
//无需声明函数原型
//...
void init()
{
//函数体
//...
}
void main()
{
//函数体
//...
init();
}
- 嵌套调用:
- 函数允许嵌套调用。
//例如:
int fun2(int m)
{
return m * m;
}
int fun1(int i,int j)
{
return fun2(i) * fun2(j);
}
void main()
{
cout << "Result is" << fun1(2,3) << endl;
}
- 递归调用:
- 函数可以直接或是间接地调用自身,称为递归调用。
//例如:
int fun(int i)
{
//...
fun(i)
//...
}
void main()
{
cout << "Result is" << fun(2) << endl;
}
- 函数参数的传递:
- 值传递:指当发生函数调用时,给形参分配内存空间,并用实参来在初始化形参。
- 引用传递:是一种特殊类型的变量,可以被认为时另一个变量的别名。
int i,j;
int &ri = i;//建立一个int型的引用ri,并将其初始化为变量i的一个别名
j = 10;
ri = j;//相当于i=j;
注意:
- 声明一个引用时,必须同时对他进行初始化,使他指向一个已存在的对象。
- 一旦一个引用被初始化后,就不能改为指向其他对象。
void swap(int &a,int &b)
{
int t = a;
a = b;
b = t;
}
void main()
{
int x = 5,y = 10;
swap(x,y);
}
内联函数
内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。
语法:
inline 类型说明符 函数名(形参列表)
(
语句序列;
)
constexpr函数
constexpr函数是指能用于常量表达式的函数。
约定:
- 函数的返回类型以及所有的形参类型必须是常量。
- 函数体中必须有且仅有一条 return 语句。
constexpr int get_size(){return 20};
constexpr int foo = get_size();//正确:foo是个常量表达式
//如果arg是常量表达式,则len(arg)也是常量表达式
constexpr int len(arg){return get_size() * arg};
带默认形参值的函数
函数在定义是可以预先声明默认的形参值。
- 有默认值的形参必须在形参列表的最后。
- 在相同的作用域内,不允许在同一个函数的多个声明中对同一个参数的默认值重复定义,即使前后定义的值相同也不行。
int add(int x = 5,int y = 6)
{
return x * y;
}
void main()
{
add(10,20); //200
add(10); //60
add(); //30
}
函数的重载
两个以上的函数,具有相同的函数名,但是形参的个数或者类型不同,编译器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。
类与对象
面向对象的基本特点
- 抽象:对具体问题(对象)进行概括,抽出一类对象的公共性质并加以描述的过程。
- 封装:将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的函数代码进行有机地结合,形成类,其中的数据和函数都是类的成员。
- 继承:一段程序能够处理多种类型对象的能力。
类和对象
在面向对象程序设计中,程序模块是由类构成的,类是对逻辑上相关的函数与数据的封装,他是对问题的抽象描述。
- 类的定义
class 类的名称
{
public:
外部接口
protected:
保护型成员
private:
私有成员
}
- 类成员的访问控制
- 访问控制属性有三种:公有类型(public)、私有类型(private)、保护类型(protected)
- 对象:声明一个对象和声明一个一般变量相同。语法:
类名 对象名; - 类的成员函数的实现:
返回值类型 类名::成员函数名(形参列表)
{
函数体;
}
- 内联成员函数
class 类名
{
public:
void 成员函数()//内联函数
{
函数体;
}
}
构造函数和析构函数
- 构造函数:构造函数的作用就是在对象被创建时利用特定的值构造对象(构造函数在对象被创建时将被自动调用),将对象初始化为一个特定的状态。
- 复制构造参数:一种特殊的构造函数,其作用是使用一个已经存在的对象(由复制构造参数的参数指定),去初始化同类的一个新对象。
- 复制构造函数在以下三种情况会被调用:
- 当用类的一个对象去初始化另一个对象时。
- 如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。
- 如果函数的返回值是类的对象,函数执行完成返回调用者时。
- 复制构造函数在以下三种情况会被调用:
- 默认构造参数:调用时无须提供参数的构造函数称为默认构造函数。
- 复制构造参数:一种特殊的构造函数,其作用是使用一个已经存在的对象(由复制构造参数的参数指定),去初始化同类的一个新对象。
class 类名
{
public:
类名(); //构造函数
//=default:指示编辑器生成默认的构造函数
类名() = default; //默认构造函数
类名() = delete; //删除默认构造函数
类名(形参列表); //默认构造函数
类名(类名 &对象名); //复制构造函数
类名():类名(形参列表); //委托构造参数:类名() -> 类名(形参列表)
private:
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
}
类名::类名(形参列表):成员变量1(形式参数1),成员变量2(形式参数2),...,成员变量n(形式参数n)
{
函数体;
}
类名::类名(类名 &对象名)
{
函数体;
}
void 函数1(类名 形参名)(函数体;)
类名 函数2()
(
return 类名();
)
void main()
{
//复制构造函数被调用的第一种情况
类名 对象名1();
类名 对象名2(对象名1); //用对象1初始化对象2,复制构造函数被调用
类名 对象名3 = 对象名1; //用对象1初始化对象3,复制构造函数被调用
cout << 对象2.成员函数1() << endl;
//复制构造函数被调用的第二种情况
//函数的形参为类的对象,当调用函数时,复制构造函数被调用
函数1(对象名1);
//复制构造函数被调用的第三种情况
类名 对象名();
对象名 = 函数2();
}
- 析构函数:用来完成对象被删除前的一些清理工作。(在对象的生存期即将结束的时候被自动调用)
class 类名
{
public:
类名();
~类名();
}
- 移动构造函数:
- 复制构造函数通过复制的方式构造新的对象,而很多时候被复制的对象仅作复制之用后销毁,在这时,如果使用移动已有对象而非复制对象将大大提高性能。
- C++11标准中引入看左值和右值。
- 左值:位于赋值语句左侧的对象变量
- 右值:赋值语句右侧的值
float n = 6;
float &lr_n = n; //对变量n的左值引用
float &&rr_n = n; //错误,不能将右值引用绑定到左值n上
float &&rr_n = n * n; //将乘法结果右值绑定到右值引用
float &lr_n = n * n; //错误,不能将左值引用绑定到乘法结果右值
//使用标准库 utility 中声明提供了 move 函数,将左值对象移动成为右值
float &&rr_n = std:move(n);
基于右值引用的新设定,可以通过移动而不复制实参的高性能方式构建新对象,即移动构造函数。类似于复制构造函数的参数为该类对象的右值引用,在构造中移动源对象资源,构造后源对象不再指向被移动的资源,源对象可重新赋值或者被销毁。
class Mystr
{
public:
string s;
Mystr():s(""){};
Mystr(string _s):s(std:move(_s)){};
Mystr(Mystr &&str) noexcept :s(std:move(str.s)){} //告知编译器不会抛出异常,移动构造函数
}
结构体和联合体
结构体和类的唯一区别在于,结构体和类具有不同的默认访问控制属性:在类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型;在结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为共有类型。
语法:
//定义语法:
struct 结构体名称
{
公有成员
protected:
保护型成员
private:
私有成员
}
//使用语法:
结构体名称 变量名 = {成员数据1初值,成员数据2初值,成员数据3初值,...};
示例:
struct Studentt
{
int num;
string name;
char sex;
int age;
}
void main()
{
Student stu = {97001,"Lin Lin",'F',19}
cout << stu.name << endl;
}
联合体的全部数据成员共享一组内存单元。
定义语法:
union 联合体名称
{
公有成员
protected:
保护型成员
private:
私有成员
}
例如:
union Mark
{
char grade;
bool pass;
int percent;
}
联合体的一些限制:
- 联合体的各个对象成员,不能有自定义的构造函数、自定义的析构函数和重载的赋值运算符,不仅联合体的对象成员不能有这些函数,这些对象成员的对象成员也不能有。
- 联合体不能继承,因而也不支持包含多态。
联合体也可以不声明名称,称为无名联合体。
例如:
class ExamInfo
{
public:
ExamInfo(string _name,char _grade):name(_name),grade(_grade),mode(GRADE){};
ExamInfo(string _name,bool _pass):name(_name),pass(_pass),mode(PASS){};
ExamInfo(string _name,int _percent):name(_name),percent(_percent),mode(PERCENTAGE){};
void show();
private:
string name;
enum
{
GRADE,
PASS,
PERCENTAGE
} mode,
union
{
char grade,
bool pass,
int percent
}
};
void ExamInfo::show()
{
cout << name << ";"
switch (mode)
{
case GRADE:cout << grade;break;
case PASS:cout << (pass ? "PASS" : "FAIL");break;
case PERCENTAGE:cout << percent;break;
}
cout << endl;
}
int main()
{
ExamInfo course1("English",'B');
ExamInfo course2("Calculus",true);
ExamInfo course3("C++ Programming",85);
course1.show()
course2.show()
course3.show()
}
枚举类型 enum
C++语言包含两种枚举类型:不限定作用域的枚举类型和限定作用域的枚举类型。
声明语法:
enum [枚举名]
{
变量值列表;
}
//枚举类声明形式如下:
enum class/struct [枚举名]
{
变量值列表;
}
例如:
enum Weekday {SUN,MON,TUE,WED,THU,FRI,SAT}
//枚举类
enum class Weekday {SUN,MON,TUE,WED,THU,FRI,SAT}
- 枚举类型的名字是可选的。如果 enum 未命名,我们只能在定义该 enum 时定义它的对象,即需要在 enum 定义右侧的花括号和最后的分号之间提供声明列表(使用逗号隔开)。
- 如果没有指定 enum 的潜在类型,默认情况下限定作用域的成员类型为 int ,对于不限定作用域的 enum 来说,其枚举成员不存在默认类型。
- 如果指定了枚举元素的潜在类型,一旦某个枚举元素的值超出了该类型范围,会引发程序错误。
由于不限定作用域的 enum 没有指定成员的默认类型,因此必须显示指定。
语法:
enum 枚举名:变量类型
例如:
enum Weekday: string; //不限定作用域的,必须指定成员类型
enum Weekday; //限定作用域的,可使用默认成员类型
枚举类型运用说明:
- 对枚举元素按常量处理,不能对他们赋值。
- 枚举元素具有默认值,他们依次为:0,1,2,…。
- 也可以在声明时另行定义枚举元素的值。
enum Weekday {SUN = 7,MON = 1,TUE,WED,THU,FRI,SAT}
//定义SUN为7,MON为1,以后的顺序加1,枚举值可以不唯一。
- 枚举值可以进行关系运算
- 整数值不能直接赋值给枚举变量,如需将整数赋值给枚举变量,应进行强制类型的转换。
- 枚举元素是 const 类型,因此初始化枚举成员是提供的初始值必须是常量表达式。
enum Weekday {SUN,MON,TUE,WED,THU,FRI,SAT}
//显示类型转换
Weekday res = Weekday(1); //结果为 SUN
标识符的作用域与可见性
作用域
作用域是一个标识符在程序正文中有效的区域。
- 函数原型作用域:在函数原型声明时形式参数的作用范围就是函数原型的作用域。
- 局部作用域:函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的花括号为止。
- 类作用域:
- 如果 X 的成员函数中没有声明同名的局部作用域标识符,那么该函数内可以直接访问成员 m 。
- 通过表达式 x.m 或者 X::m 。这正是程序中访问对象成员的最基本方法。
- 通过 ptr -> m 这样的表达式,其中 ptr 为指向 X 类的一个对象指针。
- 文件作用域:不在前述各个作用域中出现的声明,就具有文件作用域,这样的标识符其作用开始于生命点,结束于文件尾。(具有文件作用域的变量也称为全局变量)
- 命名空间作用域。
- 限定作用域的 enum 枚举类。
可见性
程序运行到某一点,能够引用到的标识符,就是该处可见的标识符。
对象的生存期
静态生存期
如果对象的生存期与程序的运行期相同,我们称他具有静态生存期。
特点:他并不会随着每次函数调用而产生一个副本,也不会随着函数返回而失效。即当一个函数返回后,下一次在调用时,该变量还会保持上回的值,即使发生了递归调用,也不会为该变量建立新的副本,该变量会在各次调用间共享。
动态生存期
在局部作用域中声明的具有动态生存期的对象,习惯上也被称为局部生存期对象。局部生存期对象诞生于声明点,结束于声明所在的块执行完毕之时。
//例如:
//...
int i = 1;//全局变量,具有静态生存期
void other()
{
//静态局部变量,具有全局寿命,局部可见,只有第一次进入函数时被初始化
static int a = 2;
static int b;
//局部变量,具有动态生存期,每次进入函数时都初始化
int c = 10;
}
类的静态成员
静态数据成员
- 具有静态生存期
- 类属性是描述的所有对象共同特征的一个数据项,对于任何对象实例,他的属性值都是相同的。
- 静态数据成员声明后,只能在类外才能进行赋值!常量静态数据成员在类内进行赋值初始化!
class Point
{
public:
void add()
{
count++;
}
private:
static int count;
constexpr static int origin = 0;//静态常量在类内初始化
}
int Point::count = 0;
void main()
{
Point p;
p.add();
}
静态函数成员
- 静态成员函数可以直接访问该类的静态数据和函数成员。而访问非静态成员,必须通过对象名来访问。
class Point
{
public:
void add()
{
count++;
}
static void showCount();
private:
static int count;
int x;
constexpr static int origin = 0;//静态常量在类内初始化
}
int Point::count = 0;
static void Point::showCount()
{
cout << "showCount is" << count << endl;
}
static void Point::f(Point p)
{
cout << "x is" << x << endl;//对x的引用是错误的
cout << "x is" << p.x << endl;//正确
}
void main()
{
Point p1;
Point p;
p.add();
//直接通过类名调用函数输出对象个数的初始值
Point::showCount(p1);
Point::f();
//也可以通过对象来调用
p.showCount();
}
类的友元
友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。
友元函数
友元函数是在类中用关键字 friend 修饰的非成员函数。虽然他不是本类的成员函数,但是他在函数体中可以通过对象名访问类的私有和保护成员。
class Point
{
public:
Point(int x = 0,int y = 0);
friend float dist(Point &p);
private:
int x,y;
}
friend float dist(Point &p)//友元函数的实现
{
return static_cast<float>(sprt(p.x * p.y))
}
void main()
{
Point p(5,3);
cout << dist(p) << endl;//结果:15
}
友元类
同友元函数一样,若A类为B类的友元函数,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。
class A
{
public:
void display()
{
cout << x << endl;
}
int getX(){return x;}
firend class B;
private:
int x;
}
class B
{
public:
void set(int i);
void display();
private:
A a;
}
void B::set(int i)
{
a.x = i;
}
注意:
- 友元关系是不能传递的。
- 友元关系是单向的。
- 友元关系是不能被继承的。
共享保护数据
常对象
常对象的数据成员值在对象的整个生存期内不能被改变,即常对象必须进行初始化,而且不能被更新。
声明语法:
const 类型说明符 对象名;
//或
类型说明符 const 对象名;
//例如:
const A a(1,3);
用 const 修饰的类成员
- 常成员函数(不可以在类内进行修改值):
- 使用 const 关键字修饰的函数为常成员函数
- 作用:可以区分重载函数。
- 声明语法:
类型说明符 函数名(形参列表) const;
//例如:
class A
{
public:
void display()
{
cout << x << endl;
}
int getX() const;
private:
int x;
}
int A::getX() const
{
return x;
}
- 常数据成员
- 常数据成员只能通过构造函数进行赋值。
- 静态常数据成员在类外说明和初始化。
- 例如:
class A
{
public:
A(int x);
void display()
{
cout << x << endl;
}
private:
const int x;
static const int y;
}
//静态常成员数据在类外说明和初始化
const int A::y = 10;
//常数据成员只能通过初始化列表来获得初始值
A::A(int _x):x(_x){};
常引用
如果在声明引用时用 const 修饰,被声明的引用就是常引用。
注意:常引用所引用的对象不能被更新。
class Point
{
public:
Point(int x = 0,int y = 0);
friend float dist(Point &p);
private:
int x,y;
}
friend float dist(Point &p)//友元函数的实现
{
//static_cast:强制转换
return static_cast<float>(sprt(p.x * p.y))
}
void main()
{
Point p(5,3);
cout << dist(p) << endl;//结果:15
}
多文件结构
- Point.h:
class Point
{
public:
Point(int _x = 0,int _y = 0):x(_x),y(_y){ count++; };
Point(const Point &p);
private:
int x,y,count;
}
- Point.cpp:
#include "Point.h"
#include <iostream>
using namespace std;
Point::Point(const Point &p):x(p.x),y(p.y)
{
count++;
}
- main.cpp:
//...
void main()
{
Point p(5,6);
}
//...
外部变量与外部函数
如果一个变量或函数除了在定义他的源文件中可以使用外,还能被其他文件使用,那么就称之为外部变量或函数。
将函数和变量限制在编译单元内:
例如:
namespace {
int n;
void f(){
n++;
}
}
编译预处理
- #include:
- #include <文件名>:按标准方式搜索,文件位于系统目录的 include 子目录下。
- #include ”文件名“:首先在当前目录中搜索,若没有,再按标准方式搜索。
- #define和#undef:
- #define 用来定义符号常量,如:
#define PI 3.1415926 - #undef 用来删除 #define 定义的宏,使之不在起作用。
- #define 用来定义符号常量,如:
- 条件编译指令:
#if 常量表达式
程序块:当常量表达式非零时编译本程序块
#endif
#if 常量表达式
程序块:当常量表达式非零时编译本程序块
#else
程序块:当常量表达式非零时编译本程序块
#endif
#if 常量表达式
程序块:当常量表达式非零时编译本程序块
#elif 常量表达式
程序块:当常量表达式非零时编译本程序块
.
.
.
#endif
- defined 操作符:
- 是一个预处理操作符,而不是指令,因此
不要以#开头 - 语法:
defined(标识符) - 若标识符在此前经 #define 定义过,并且未经 #undef 删除,则此上述表达式为非0,反之。
- 是一个预处理操作符,而不是指令,因此
#ifndef MyCPP_H
#define MyCPP_H
...
#endif
//等价于
#if !defined(MyCPP_H)
#define MyCPP_H
...
#endif


![[学习笔记 Day02]Vue基础:前端造梦,继续干!-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/09/20250919193418264.jpeg)

![[学习笔记 Day01]C#基础:从入门到精通,开启新世纪的编程大门到精通,开启新世纪的编程大门-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/08/20250827185617438.jpeg)





![[自动化 + 手残党专属]宝塔安装AllinSSL证书管理教程-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/11/20251112122722716.png)




暂无评论内容