c++第七天--特殊运算符重载

发布于:2025-06-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

含有的主要内容:

类型转换函数

转换构造函数

仿函数-重载()

智能指针 -- 重载 * 和 -> 运算符

一、类型转换函数

  类型构造函数用于将一个类的对象转换为其他类型(基本类型或其他类类型)。它是类的成员函数,允许自定义对象在需要目标类型的上下文中自动(或者显式)转换。

#include <iostream>
using namespace std;

class MyInt {
private:
    int value;
public:
    // 类型转换构造函数:可以用int直接初始化MyInt对象
    MyInt(int v) : value(v) {
        cout << "类型转换构造函数被调用" << endl;
    }
    void show() const {
        cout << "value = " << value << endl;
    }
};

int main() {
    MyInt a = 5;      // int -> MyInt,调用类型转换构造函数
    a.show();

    MyInt b(10);      // 也会调用类型转换构造函数
    b.show();

    return 0;
}
MyInt(int v) : value(v) {
    cout << "类型转换构造函数被调用" << endl;
}

MyInt(int v) 这是一个构造函数,他接受一个int 类型的参数,因为它只有一个参数,所以被称为类型转换构造函数。 : value(v) - 这是构造函数的初始化列表,它将传入的整数赋值给私有变量 value

<<

  1. { cout << "类型转换构造函数被调用" << endl; } - 构造函数的函数体,它输出一条消息表示构造函数被调用了。

  2. 关键作用:这个构造函数允许我们用整数直接初始化或赋值给 MyInt 对象,如 MyInt a = 5; 或 MyInt b(10);。编译器会自动将整数转换为 MyInt 对象

//将自定义类 Fraction 转换为 double
#include<iostream>
using namespace std;

class Fraction{
public:
Fraction(int numer = 0,int den = 1) : numerator(numer),denominator(den){
    if(denominator == 0)
    {
        cout << "Error: denominator is zero." << endl;
        denominator = 1; 
    }
}
operator double(){
    return static_cast<double>(numerator) / denominator;
}

private:
    int numerator;
    int denominator;
};


int main() {
	Fraction f(3, 4);
	cout << 2.1 + f << endl;
	return 0;
}

显式类型转换函数

 #include <iostream>
using namespace std;
class Fraction {
public:
	Fraction(int numer = 0, int den = 1) : numerator(numer), denominator(den) {
		if (denominator == 0)
		{
			cout << "Error: denominator is zero." << endl;
			denominator = 1;
		}
	}
	explicit operator double() {
		return static_cast<double>(numerator) / denominator;
	}
private:
	int numerator;  // 分子
	int denominator;  // 分母
};


int main() {
	Fraction f(3, 4);
	cout << 2.1 + static_cast<double>(f) << endl;
	return 0;
}

// 这里的 [operator double()]显式类型转换函数.cpp )
// 函数被声明为 explicit(显式),这意味着编译器不允许进行隐式转换。所以你必须使用显式类型转换操作符
// (如 static_cast)来明确表示你要将分数对象转换为 double 类型。

在代码中使用 static_cast<double>(f) 是因为:

这里的 [operator double()]显式类型转换函数.cpp ) 函数被声明为 explicit(显式),这意味着编译器不允许进行隐式转换。所以你必须使用显式类型转换操作符(如 static_cast)来明确表示你要将分数对象转换为 double 类型。

转换构造函数

  转换构造函数是一种特殊的构造函数,他可以将其他类型的数据转换为当前类的对象,转换构造函数通常为当前类的对象。转换构造函数通常只有一个参数,改参数的类型就是要转换的类型。

转换构造函数是一种特殊的构造函数,他可以将其他类型的数据转换为当前类的对象,他的主要特点:

1.只有一个参数(或者除了第一个参数外,其他参数为默认值)

2.不使用 explict 关键字修饰,(这样才能实现隐式转换)

3. 目的是为了实现其他类型到当前类型的转换

//编写 Distance 类。能够将整数类型转换为 Distance 对象

#include<iostream>
using namespace std;

class Distance{
private:
    int meters;
public:
    Distance(int m) : meters(m){
        std::cout << "转换构造函数被调用" << std::endl;
    }
    void display(){
        std::cout << "Distance" << meters << "meters" << std::endl;
    }
};

int main()
{
    int value = 100;
    Distance d1 = value;
    Distance d2(200); //这里就是直接进行初始化
    Distance d3 = 300; //临时对象创建并赋值

    d1.display();
    d2.display();
    d3.display();

    //在赋值操作中也可以调用转换构造函数
    d1 = 400;
    d1.display();

    return 0;
}

同样,如果在转换构造函数之前加上explicit,就只能显示转换。

#include<iostream>
using namespace std;

class Distance {
private:
    int meters;
public:
    explicit Distance(int m) : meters(m){
        std::cout << "转换构造函数被调用" << std::endl;
    }
void display(){
    std::cout << "Distamce:" << meters << " meters" << std::endl;
}
};

int main()
{
    int value = 100;

    Distance d = static_cast<Distance>(value);
    d = static_cast<Distance>(200);

    d.display();
    return 0;
}

类型转换函数和构造函数是双向转换的核心:

仿函数

 仿函数,也称为函数对象,是一种重载了函数调用运算符()的类或结构体对象。

()除了改变运算优先级之外,放在函数名后面表示调用函数。

C++仿函数(函数对象)详解

仿函数(Functor)也称为函数对象(Function Object),是C++中一种特殊的设计模式,它允许一个类的对象像函数一样被调用。通过重载类的operator()运算符,我们可以使类的对象在语法上表现得像函数一样。

仿函数的基本概念

仿函数是通过重载()操作符(函数调用操作符)实现的,这使得类的对象可以像函数一样使用。与普通函数相比,仿函数有以下优势:

  1. 可以保持状态:仿函数可以在类中定义成员变量来存储状态信息
  2. 可以拥有类型:作为一个类,仿函数具有自己的类型
  3. 可以携带附加信息:通过成员变量或成员函数提供更多功能
  4. 通常比普通函数效率更高:编译器可以更好地优化内联函数对象

//仿函数,主要是通过重载()操作符实现的,这使得类的对象可以像函数一样调用,主要是通过operator()运算符
#include<iostream>
using namespace std;

//定义一个仿函数类
class AddNumber{
private:
    int number;
public:
    //构造函数,初始化状态
    AddNumber(int n) : number(n) {}

    //重载()操作符,使对象可以像函数一样调用
    int operator() (int x) const{
        return x + number;
    }
};

int main()
{
//创建函数对象
AddNumber add5(5);

//使用函数对象,就像调用函数一样

cout << "10 + 5 = " << add5(10) << endl;

//创建另一个函数对象
AddNumber add10(10);
cout << "20 + 10 = " << add10(20) << endl;

//临时函数对象
cout << "7 + 15 = " << AddNumber(15)(7) << endl;

return 0;
}

  虽然它是对象,但使用起来却像函数一样,可以像调用函数一样调用仿函数对象。

  仿函数可以拥有成员变量,这些成员变量能够保存状态信息,在不同的调用中可以保持和使用这些状态,而普通函数通常无法做到这一点。

//仿函数,主要是通过重载()操作符实现的,这使得类的对象可以像函数一样调用,主要是通过operator()运算符
#include<iostream>
using namespace std;

//定义一个仿函数类
class AddNumber{
private:
    int number;
public:
    //构造函数,初始化状态
    AddNumber(int n) : number(n) {}

    //重载()操作符,使对象可以像函数一样调用
    int operator() (int x) const{
        return x + number;
    }
};

int main()
{
//创建函数对象
AddNumber add5(5);

//使用函数对象,就像调用函数一样

cout << "10 + 5 = " << add5(10) << endl;

//创建另一个函数对象
AddNumber add10(10);
cout << "20 + 10 = " << add10(20) << endl;

//临时函数对象
cout << "7 + 15 = " << AddNumber(15)(7) << endl;

return 0;
}

智能指针

在C++中没有垃圾回收机制,堆内存资源的使用和释放需要自己编写程序实现,编写大型的程序可能会忘记释放内存导致内存泄漏。为了解决这个问题,C++标准提出了智能指针这种机制,解决了编程中堆内存泄漏的问题。智能指针的本质是使用引用计数的方式解决悬空指针的问题,通过重载*和->运算符实现。

一个类如果重载了*和->运算符,那么这个类的对象就好像指针那样使用了。而对象的管理是由系统自动管理的,不需要人为的delete,也不会漏掉delete.

#include<iostream>
using namespace std;
class Data{
public:
    void dis() {cout << "data" << endl;}
    ~Data() {cout << "data destructor" << endl;}
};

class SmartDataPtr{
public:
    SmartDataPtr(Data* pdata = 0) : pdata(pdata){
        if(pdata) 
            refCount = new int(1);
        else 
        refCount = 0;
    }
    SmartDataPtr(const SmartDataPtr& p) : pdata(p.pdata),refCount(p.refCount){
        if(refCount)
         ++*refCount;
    }
    //赋值运算符
    SmartDataPtr& operator=(const SmartDataPtr& p){
        if(this != &p){
            decount();  //先减少源资源的引用计数
            pdata = p.pdata;
            refCount = p.refCount;
            if(refCount)
                ++*refCount;  //增加共享资源的引用计数
        }
        return*this;
    }
    void decount() {
    if (refCount) {
        --*refCount;
        if (*refCount == 0) {
            delete refCount;
            delete pdata;
        }
    }
}
~SmartDataPtr() { decount(); }
Data& operator*() { return *pdata; }
Data* operator->() { return pdata; }
private:
    int* refCount;  // 引用计数,这里是用于记录有多少个智能指针指向同一资源
    Data* pdata;    // 指向实际数据的指针
};

int main() {
    {
        SmartDataPtr p1(new Data());  // 创建第一个智能指针,引用计数为1
        SmartDataPtr p2(p1);          // 创建第二个智能指针,引用计数变为2
        SmartDataPtr p3;              // 创建空智能指针
        p3 = p1;                      // 赋值后,引用计数变为3
        p3->dis();                    // 通过->访问对象的dis方法
    }  // 所有智能指针离开作用域,引用计数归零,Data对象被释放
    
    int x;
    cin >> x;
    return 0;
}

为什么在赋值操作时需要调用decount()

在智能指针的赋值运算符重载中,decount()这一步是非常关键的。我来详细解释为什么需要这一步操作

赋值操作的本质

当我们执行赋值操作如p3 = p1;时,实际上发生了两个关键步骤:

  1. 放弃旧资源p3需要停止引用它当前指向的资源
  2. 获取新资源p3需要开始引用p1所指向的资源

为什么需要decount()

decount()调用处理的是第一步 - 放弃旧资源。它有两个重要作用:

1. 减少旧资源的引用计数

如果p3之前已经指向了某个资源,那么当p3要指向新的资源时,必须先通知系统它不再使用旧资源。这是通过减少旧资源的引用计数来实现的。

2. 可能释放不再使用的资源