函数的分文件编写

作用:让代码结构更加清晰

函数分文件编写一般有4个步骤

创建后缀名为.h的头文件
创建后缀名为.cpp的源文件
在头文件中写函数的声明
在源文件中写函数的定义

指针和函数

作用:利用指针作函数参数,可以修改实参的值

示例:

//值传递
void swap1(int a ,int b)
{
	int temp = a;
	a = b; 
	b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

int main() {

	int a = 10;
	int b = 20;
	swap1(a, b); // 值传递不会改变实参

	swap2(&a, &b); //地址传递会改变实参

	cout << "a = " << a << endl;

	cout << "b = " << b << endl;

	system("pause");

	return 0;
}

总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递

  • const修饰指针
    const修饰指针有三种情况

const修饰指针 --- 常量指针
const修饰常量 --- 指针常量
const即修饰指针,又修饰常量

空指针(Null Pointer)

定义:
空指针指的是 明确赋值为 nullptr(C++11及以后)或 NULL(C 语言) 的指针,表示“什么也不指”。

int* ptr = nullptr; // C++11
int* ptr = NULL;    // C 风格

常见错误:

  int* ptr = nullptr;
  *ptr = 42; // ❌ 运行时崩溃(段错误)

检查方式:

if (ptr != nullptr) {
    // safe to dereference
}

野指针(Wild Pointer)

定义:
野指针指的是:

未初始化的指针

指向已经释放的内存

它们“看起来有地址”,但 这个地址不合法或不可控,使用极其危险。

🚨 示例 1:未初始化的指针

int* p;   // ❌ 没有初始化,p 指向未知内存
*p = 10;  // ❌ 随机内存写入 → 崩溃 or 写乱内存

🚨 示例 2:悬垂指针(Dangling Pointer)

int* p;
{
    int a = 10;
    p = &a;
} // a 生命周期结束
*p = 20; // ❌ 悬垂指针,访问已销毁变量

🚨 示例 3:释放后未置空

int* p = new int(5);
delete p;
*p = 10;     // ❌ 指向已释放内存(悬垂)
p = nullptr; // ✅ 建议 delete 后立即置空

https://github.com/Blitzer207/C-Resource/blob/master/第3阶段-C%2B%2B核心编程 资料/讲义/C%2B%2B核心编程.md

引用

1.给变量起别名
2.必须先初始化
3.给函数做参数

  //1. 值传递
  void mySwap01(int a, int b) {
  	int temp = a;
  	a = b;
  	b = temp;
  }

  //2. 地址传递
  void mySwap02(int* a, int* b) {
  	int temp = *a;
  	*a = *b;
  	*b = temp;
  }

  //3. 引用传递
  void mySwap03(int& a, int& b) {
  	int temp = a;
  	a = b;
  	b = temp;
  }

  int main() {

  	int a = 10;
  	int b = 20;

  	mySwap01(a, b);
  	cout << "a:" << a << " b:" << b << endl;

  	mySwap02(&a, &b);
  	cout << "a:" << a << " b:" << b << endl;

  	mySwap03(a, b);
  	cout << "a:" << a << " b:" << b << endl;

  	system("pause");

  	return 0;
  }
  1. 值传递 mySwap01

    void mySwap01(int a, int b)
    调用时传的是 a 和 b 的副本。

函数内部修改的是 副本的值,不会影响 main 中的 a 和 b。

所以调用完后 a = 10, b = 20 不变。

🔹2. 地址传递(指针)mySwap02

void mySwap02(int* a, int* b)

调用时传的是变量 a 和 b 的地址(即 &a, &b)。

在函数中通过 a、b 来修改原值。

所以可以交换 main 中的 a 和 b。

🔹3. 引用传递 mySwap03

void mySwap03(int& a, int& b)

a 和 b 是原变量的“别名”(引用),函数内部直接操作原变量。

既没有创建副本,也没有解引用,语法更清晰,效率更高。

推荐使用。

  • 引用的本质
  1. 本质:引用的本质在c++内部实现是一个指针常量.
    **int& ref=a 是创建一个引用,也就是创建了一个a的别名。相当于 Int& const ref=a,ref的指向不能改,而可以通过改a或改ref来改变a的值。 **

讲解示例:

  //发现是引用,转换为 int* const ref = &a;
  void func(int& ref){
  	ref = 100; // ref是引用,转换为*ref = 100
  }
  int main(){
  	int a = 10;
      
      //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
  	int& ref = a; 
  	ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
      
  	cout << "a:" << a << endl;
  	cout << "ref:" << ref << endl;
      
  	func(a);
  	return 0;
  }
  1. 常量引用
    作用:常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参(指针指向的值不可改变)

示例:

//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
	//v += 10;
	cout << v << endl;
}

int main() {

	//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
	//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
	const int& ref = 10;

	//ref = 100;  //加入const后不可以修改变量
	cout << ref << endl;

	//函数中利用常量引用防止误操作修改实参
	int a = 10;
	showValue(a);

	system("pause");

	return 0;
}

对象模型和this指针

只有非静态变量才属于类内的对象,占对象空间

class Person {
public:
	Person() {
		mA = 0;
	}
	//非静态成员变量占对象空间
	int mA;
	//静态成员变量不占对象空间
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
	}
};

this指针

C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
一块代码是如何区分那个对象调用自己的呢

  1. this 的基本作用
    (1) 区分成员变量和局部变量
    当成员变量和函数参数(或局部变量)同名时,this 可以明确指定访问的是 当前对象的成员变量。

例子:Phone 类(数码产品手机)

class Phone {
private:
    string brand;  // 手机品牌
    int price;     // 价格(元)
public:
    // 构造函数(参数名和成员变量同名)
    Phone(string brand, int price) {
        this->brand = brand;  // this->brand 表示类的成员变量
        this->price = price;  // this->price 表示类的成员变量
    }

    void display() {
        cout << "Brand: " << this->brand << ", Price: " << this->price << endl;
    }
};

int main() {
    Phone iphone("Apple", 9999);
    iphone.display();  // 输出:Brand: Apple, Price: 9999
    return 0;
}

说明:

this->brand 和 this->price 明确指向 Phone 类的成员变量。

如果不加 this,编译器可能会混淆 brand 和 price 是参数还是成员变量。

(2) 返回当前对象(用于链式调用)
this 可以返回当前对象的引用,常用于 链式调用(连续调用多个方法)。

例子:SmartWatch 类(智能手表)

class SmartWatch {
private:
    int stepCount;  // 步数
    int heartRate;  // 心率
public:
    SmartWatch() : stepCount(0), heartRate(0) {}

    // 返回 this 的引用,支持链式调用
    SmartWatch& setSteps(int steps) {
        this->stepCount = steps;
        return *this;  // 返回当前对象
    }

    SmartWatch& setHeartRate(int rate) {
        this->heartRate = rate;
        return *this;  // 返回当前对象
    }

    void display() {
        cout << "Steps: " << this->stepCount << ", Heart Rate: " << this->heartRate << endl;
    }
};

int main() {
    SmartWatch watch;
    watch.setSteps(5000).setHeartRate(80);  // 链式调用
    watch.display();  // 输出:Steps: 5000, Heart Rate: 80
    return 0;
}

说明:

return *this 返回当前对象的引用,使得 setSteps() 和 setHeartRate() 可以连续调用。

  1. this 的高级用法
    (3) 在成员函数中传递当前对象
    有时候需要把当前对象作为参数传递给其他函数。

例子:Camera 类(数码相机)

class Camera {
private:
    string model;
public:
    Camera(string model) : model(model) {}

    // 模拟保存相机数据到数据库
    void saveToDatabase() {
        DatabaseManager::save(*this);  // 把当前对象传给数据库管理器
    }

    string getModel() {
        return this->model;
    }
};

class DatabaseManager {
public:
    static void save(Camera& cam) {
        cout << "Saving camera: " << cam.getModel() << endl;
    }
};

int main() {
    Camera sony("Sony Alpha A7");
    sony.saveToDatabase();  // 输出:Saving camera: Sony Alpha A7
    return 0;
}

说明:

DatabaseManager::save(*this) 把当前 Camera 对象传递给数据库管理类。