C++ 重载(Overloading)
C++ 重载(Overloading) 允许多个函数或运算符使用相同的名称,但参数不同。重载分为:
- 函数重载(Function Overloading)
- 运算符重载(Operator Overloading)
C++ 中的隐藏(Name Hiding)
C++ 隐藏(Name Hiding) 发生在子类与基类、局部变量与全局变量、作用域间的同名标识符之间。隐藏使得基类的成员或外层作用域的变量无法直接访问,通常发生在变量和函数名相同的情况下。
🚀 重载的主要目的是提高代码的可读性和可维护性,避免定义多个相似的函数名称。
1. 函数重载(Function Overloading)
📌 规则
- 函数名相同,但参数列表必须不同:
- 参数个数不同
- 参数类型不同
- 参数顺序不同
- 不能仅靠返回类型区分重载(编译器不会仅根据返回值判断函数)。
✅ 示例
#include <iostream>
// 重载 `add` 函数
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
int main() {
std::cout << add(1, 2) << std::endl; // 调用 `int add(int, int)`
std::cout << add(1.5, 2.5) << std::endl; // 调用 `double add(double, double)`
std::cout << add(1, 2, 3) << std::endl; // 调用 `int add(int, int, int)`
}
📌 同名 add
函数根据参数类型自动匹配合适的版本。
2. 构造函数重载
📌 类的构造函数可以重载,以支持不同的初始化方式。
✅ 示例
#include <iostream>
class Car {
public:
Car() { std::cout << "Default constructor\n"; } // 默认构造
Car(int speed) { std::cout << "Speed: " << speed << "\n"; } // 传参构造
};
int main() {
Car car1; // 调用默认构造函数
Car car2(100); // 调用带参数的构造函数
}
📌 构造函数重载允许对象以不同方式初始化,提高灵活性。
3. 运算符重载(Operator Overloading)
📌 C++ 允许重载运算符,使其适用于自定义类。
✅ 示例
#include <iostream>
class Complex {
public:
int real, imag;
Complex(int r, int i) : real(r), imag(i) {}
// ✅ 重载 `+` 运算符
Complex operator+(const Complex& other) {
return Complex(real + other.real, imag + other.imag);
}
void display() { std::cout << real << " + " << imag << "i\n"; }
};
int main() {
Complex c1(2, 3), c2(4, 5);
Complex c3 = c1 + c2; // ✅ `operator+` 被调用
c3.display(); // 输出:6 + 8i
}
📌 运算符重载 operator+
让 Complex
类型支持 +
号运算。
4. <<
重载(用于 std::cout
)
#include <iostream>
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
// ✅ 重载 `<<` 让 `std::cout` 支持 `Point`
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << "(" << p.x << ", " << p.y << ")";
}
};
int main() {
Point p(3, 4);
std::cout << p << std::endl; // ✅ 自动调用 `operator<<`
}
📌 重载 <<
使 std::cout
可以直接输出 Point
对象。
5. 不能重载的运算符
🚨 C++ 允许重载大部分运算符,但以下运算符 ❌ 不能重载:
::
(作用域解析符).*
(成员指针访问)sizeof
(获取类型大小)typeid
(获取类型信息)alignof
(获取对齐方式)new
和delete
(只能作为类成员重载)
6. 重载 vs. 默认参数
📌 默认参数和重载类似,但它们的适用场景不同。
void print(int x, int y = 10) {
std::cout << x << ", " << y << std::endl;
}
✅ 适用于 参数个数不同 的情况,减少函数重载的数量。
7. 关键总结
类型 | 作用 | 示例 |
---|---|---|
函数重载 | 允许同名函数不同参数 | void add(int) vs void add(double) |
构造函数重载 | 允许对象不同方式初始化 | Car() vs Car(int speed) |
运算符重载 | 允许类支持 + 、<< 等 | Complex operator+(const Complex&) |
🚀 C++ 通过重载提高代码的可读性和灵活性,是面向对象编程的关键特性! 🚀
C++ 中的隐藏(Name Hiding)
C++ 隐藏(Name Hiding) 发生在子类与基类、局部变量与全局变量、作用域间的同名标识符之间。隐藏使得基类的成员或外层作用域的变量无法直接访问,通常发生在变量和函数名相同的情况下。
1. 变量隐藏(局部变量 vs. 全局变量)
📌 局部变量会隐藏全局变量,即使名称相同。
#include <iostream>
int x = 10; // ✅ 全局变量
int main() {
int x = 20; // ✅ 局部变量,隐藏全局变量
std::cout << x << std::endl; // ✅ 输出 20(局部变量)
std::cout << ::x << std::endl; // ✅ `::x` 访问全局变量,输出 10
}
📌 局部变量 x
会隐藏全局变量 x
,但可以用 ::x
访问全局变量。
2. 基类和派生类的隐藏
📌 如果子类定义了与基类同名的变量或函数,基类的相应成员会被隐藏,即使参数列表不同。
(1)基类成员变量被隐藏
#include <iostream>
class Base {
public:
int x = 10;
};
class Derived : public Base {
public:
int x = 20; // ✅ 隐藏 Base::x
};
int main() {
Derived obj;
std::cout << obj.x << std::endl; // ✅ 输出 20(Derived::x)
std::cout << obj.Base::x << std::endl; // ✅ 访问 Base::x,输出 10
}
📌 子类 x
隐藏了 Base::x
,但可以用 obj.Base::x
访问基类变量。
(2)基类成员函数被隐藏
#include <iostream>
class Base {
public:
void show() { std::cout << "Base show()\n"; }
};
class Derived : public Base {
public:
void show(int x) { std::cout << "Derived show(int)\n"; } // ✅ 隐藏 Base::show()
};
int main() {
Derived obj;
obj.show(5); // ✅ 调用 Derived::show(int)
// obj.show(); // ❌ 错误:Base::show() 被隐藏
}
📌 即使 Derived::show(int)
参数不同,也会隐藏 Base::show()
。
✅ 如果希望子类仍能访问基类的 show()
,需要 using
关键字:
class Derived : public Base {
public:
using Base::show; // ✅ 让 Base::show() 重新可见
void show(int x) { std::cout << "Derived show(int)\n"; }
};
📌 using Base::show;
让 Base::show()
在 Derived
里可见,可直接调用。
3. 函数重载 vs. 隐藏
📌 如果子类定义了和基类同名的函数(但参数不同),不会被视为重载,而是隐藏(Hiding),会让基类的所有重载版本都不可见。
#include <iostream>
class Base {
public:
void show() { std::cout << "Base show()\n"; }
void show(int x) { std::cout << "Base show(int)\n"; }
};
class Derived : public Base {
public:
void show(double y) { std::cout << "Derived show(double)\n"; } // ✅ 隐藏所有 Base::show()
};
int main() {
Derived obj;
obj.show(2.5); // ✅ 调用 Derived::show(double)
// obj.show(); // ❌ 错误,Base::show() 被隐藏
// obj.show(10); // ❌ 错误,Base::show(int) 也被隐藏
}
📌 Derived::show(double)
隐藏了 Base::show()
和 Base::show(int)
,即使参数不同。
✅ 解决方法:使用 using
关键字
class Derived : public Base {
public:
using Base::show; // ✅ 让所有 Base::show() 版本重新可见
void show(double y) { std::cout << "Derived show(double)\n"; }
};
📌 这样 Base::show()
和 Base::show(int)
都不会被隐藏。
4. 作用域隐藏
(1)局部变量隐藏成员变量
📌 局部变量会隐藏类的成员变量,必须用 this->
访问成员变量。
#include <iostream>
class Test {
public:
int x = 10;
void show() {
int x = 20; // ✅ 局部变量隐藏成员变量
std::cout << x << std::endl; // ✅ 输出 20(局部变量)
std::cout << this->x << std::endl; // ✅ 显式访问成员变量,输出 10
}
};
int main() {
Test obj;
obj.show();
}
📌 局部 x
隐藏了成员变量 x
,必须用 this->x
访问成员变量。
5. 关键总结
类型 | 隐藏方式 | 如何解决? |
---|---|---|
局部 vs. 全局变量 | 局部变量隐藏全局变量 | ::变量名 访问全局变量 |
子类 vs. 基类变量 | 子类变量隐藏基类变量 | 对象.Base::变量 访问 |
子类 vs. 基类函数 | 子类函数隐藏基类函数(即使参数不同) | using Base::函数名; 让基类函数可见 |
局部变量 vs. 成员变量 | 局部变量隐藏成员变量 | this->变量名 访问 |
🚀 C++ 通过 using
、作用域解析符 ::
和 this->
解决隐藏问题,让代码更清晰可读! 🚀