重载(函数重载+运算符重载)与隐藏

C++ 重载(Overloading)

C++ 重载(Overloading) 允许多个函数或运算符使用相同的名称,但参数不同。重载分为:

  1. 函数重载(Function Overloading)
  2. 运算符重载(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 (获取对齐方式)
  • newdelete (只能作为类成员重载)

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-> 解决隐藏问题,让代码更清晰可读! 🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值