【c++】cpp运算符重载

目录

(1)什么是运算符重载

(2)运算符重载的本质是函数调用

(3)可以与不可以重载的运算符

(4)单目运算符与双目符重载区别

(5)双目运算符重载举例

重载=操作符

用友元函数实现重载<<操作符

重载下标运算符[]

重载函数调用符()

(6)单目运算符重载举例

重载右值++运算符

用双目运算符的思想重载左值++运算符

重载箭头->运算符

(7)运算符重载步骤总结

(8)为什么不建议重载&&和||操作符

(9)运算符重载在项目开发中的应用

自定义字符串类

自定义智能指针类


(1)什么是运算符重载

⽤复数类举例

Complex c3 = c1 + c2;

原因 Complex是⽤户⾃定义类型,编译器根本不知道如何进⾏加减。

编译器给提供了⼀种机制,让⽤户⾃⼰去完成,⾃定义类型的加减操作等。

这个机制就是运算符重载机制。

(2)运算符重载的本质是函数调用

全局函数角度:c1 + c2 从全局函数角度等价于函数调用:operator+(c1, c2)。

声明:Complex operator+(Complex &c1, Complex &c2)  // 需要定义的全局函数

注意:运算符重载函数要操作私有成员时,需要在类中声明为友元函数。

使用:

  • Complex c4 = operator+(c1, c2);  // 正常像函数那样使用,函数名是"operator+"
  • Complex c4 = c1 + c2;   // 用符号使用

类成员函数角度:c1 - c2 从类成员函数角度 等价于函数调用:c1.operator-(c2)。

声明:Complex operator-(Complex &c2)  // 需要定义类成员函数

因为c1-c2等价于c1.operator-(c2),要想能使用c1-c2

就需要在c类的内部自定义Complex operator-(Complex &c_object)函数

再以重载=为例,A类,通过类成员函数重载:a1=a2,等价于a1.operator=(a2),所以需要在A类的内部自定义A operator=(Complex &A_object)函数。

示例代码

#include <iostream>

class Complex
{
public:
    int a;
    int b;
public:
    Complex(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void printCom()
    {
        std::cout << a << "+" << b << "i" << std::endl;
    }
public:
    // 类成员函数实现运算符重载
    Complex operator*(Complex &c2)
    {
        Complex temp(this->a * c2.a - this->b * c2.b, 
                    this->a * c2.b + this->b * c2.a);
        return temp;
    }
};

// 全局函数实现特定对象之间的运算
Complex com_add(Complex &c1, Complex &c2)
{
    Complex temp(c1.a + c2.a, c1.b + c2.b);
    return temp;
}

// 全局函数实现运算符重载
Complex operator+(Complex &c1, Complex &c2)
{
    Complex temp(c1.a + c2.a, c1.b + c2.b);
    return temp;
}

int main()
{
    Complex c1(1, 2), c2(3, 4);
    // a + bi 复数运算规则
    // 类也是一种数据类型
    // 用户自定义数据类型 两个对象之间运算 C++编译器 是不知道如何进行运算

    // 1.定义全局函数进行计算
    Complex c3 = com_add(c1, c2);
    c3.printCom(); //4+6i

    // 2.运算符重载
    // c++编译器应该给我们程序员提供一种机制
    // 让自定义数据类型 有机会 进行 运算符操作 ====> 运算符重载机制

    // 全局函数实现
    Complex c4 = operator+(c1, c2);
    c4.printCom(); // 4+6i

    Complex c5 = c1 + c2;
    c5.printCom(); // 4+6i

    // 类成员函数实现
    Complex c6 = c1 * c2;
    c6.printCom(); //-5+10i

    return 0;
}

(3)可以与不可以重载的运算符

可以重载的运算符:

+-*/%^&|~
!=<>+=-=*=/=%
^=&=|=<<>>>>=<<===!=
<=>=&&||++--->*->[ ]
()newdeletenew[]delet[]

不能重载的运算符

.::.*?:sizeof

(4)单目运算符与双目符重载区别

单目运算符:比如++,像a++,++b等。

双目运算符:比如+, =, ==,像a+b,c=d,c==d等。

运算符重载函数:[返回值] operator[运算符] (参数...) { ... };

对于参数:重载函数的参数个数必须与运算符原来的个数一致。比如+号的参数就是左加数和右加数两个。那么当我们重载加号时也要保证有左右两个加数作为参数。

对于双目运算符

  • 用全局函数重载时,左边形参作为运算符左操作数,右边形参是右操作数。

  • 用类成员函数重载时,只需要写一个参数即可,因为类的成员函数默认有一个this指针,有一个参数就已经被this指针包含了,this指向运算符左参数。以减号为例,两个时间类a和b相减时。如果是a - b,那么this指针指向a,反之则指向b。在声明函数时,我们只需要写右参数即可。a - b的话只需要写 int operator-(Time b);

对于单目运算符

运算符在左侧,参数在右侧,比如++a(说明:a++这种比较特殊,运算符是在右侧,这种用用特殊技巧重载)

  • 用全局函数重载时,右操作数是形参。
  • 用类成员函数重载时,单目运算符由于只有一个参数,且该参数被this所指向,那么我们无需声明任何参数即可。this所指向运算符右参数。

对于返回值:运算符的返回值类型取决于该重载函数的作用是什么。

  • 比如a + b的作用是得到一个加数,那么返回值就是a+b的值。a += b的作用是让a的值改变,既然是让参数a的值改变,那么就无需返回值。
  • 还有就是如果我们需要运算符支持多次操作那么也需要返回值。比如流插入运算符<<。我们可能需要多次进行插入,像cout << a << b << c;之类就需要返回流ostream本身以便于之后的流插入工作。

(5)双目运算符重载举例

重载=操作符

示例代码

#include <iostream>
#include <stdlib.h>
#include <cstring>

class Name
{
public:
    Name(const char *p, int index)
    {
        m_len = strlen(p);
        m_p = (char *)malloc(m_len + 1);
        strcpy(m_p, p);

        this->index = index;
    }
    Name(const Name &name)
    {
        std::cout << "copy generate func" << std::endl;
        m_len = name.m_len;
        m_p = (char *)malloc(m_len + 1);
        strcpy(m_p, name.m_p);

        this->index = 0;
    }
    /*
        重载=运算符
        首选因为obj3 = obj1; 所以obj3.operator=(obj1)
        再因为有链式编程 obj1 = obj2 = obj3的需要,所以需要返回引用
    */
    Name &operator=(Name &name)
    {
        std::cout << "operator= func" << std::endl;
        if (this->m_p != NULL)
        {
            free(m_p);
            m_p = NULL;
            m_len = 0;
        }
        this->m_len = name.m_len;
        this->m_p = (char *)malloc(m_len + 1);;
        strcpy(m_p, name.m_p);

        // 返回左操作参数的引用
        std::cout << getIndex() << std::endl;
        return *this;
    }
    ~Name()
    {
        if (m_p != NULL)
        {
            free(m_p);
            m_p = NULL;
            m_len = 0;
        }
    }
public:
    void printInfo()
    {
        std::cout << "&(*this)=" << this 
                  << ", name=" << this->m_p 
                  << ", index=" << this->index
                  << std::endl;
        // &(*this) = this的值
    }
    void setIndex(int index){this->index=index;}
    int getIndex(){return index;}
protected:
private:
    char *m_p;
    int m_len;
    int index;
};

void objplaymain()
{
    std::cout << "-------------A--------------" << std::endl;
    Name obj1("hello world", 1);
    Name obj2 = obj1;  // 调用拷贝构造函数
    obj2.setIndex(2);
    obj2.printInfo(); 

    std::cout << "-------------B--------------" << std::endl;
    Name obj3("obj3", 3);
    obj3 = obj1;  // 调用运算符重载函数  
    // obj3.operator=(obj1)
    obj3.printInfo(); 

    std::cout << "-------------C--------------" << std::endl;
    Name obj4("obj4", 4);
    obj1 = obj4 = obj3;  // 调用运算符重载函数

    //思考obj1 = obj4 = obj3;的运行过程!
    //从右到左,先obj4 = obj3,同时根据类中=重载定义,返回的是obj4
    //然后obj1 = obj4,返回obj1
}

int main()
{
    objplaymain();
    return 0;
}

运行结果

-------------A--------------
copy generate func
&(*this)=0x7ffd72212180, name=hello world, index=2
-------------B--------------
operator= func
3
&(*this)=0x7ffd72212170, name=hello world, index=3
-------------C--------------
operator= func
4
operator= func
1

用友元函数实现重载<<操作符

当⽆法修改左操作数的类时,使⽤友元函数进⾏重载

istream 和 ostream 是 C++ 的预定义流类,cin 是 istream 的对象,cout 是 ostream 的对象。

运算符 << 由ostream 重载为插⼊操作,⽤于输出基本类型数据。

运算符 >> 由 istream 重载为提取操作,⽤于输⼊基本类型数据。

⽤友员函数重载 > ,输出和输⼊⽤户⾃定义的数据类型。

由于编译器ostream源码我们是拿不到的,所以不能通过设置成员函数来重载<<运算符,这里可以通过友元函数实现。

示例代码

#include <iostream>

class Complex
{
private:
    int a;
    int b;
public:
    // friend void operator<<(std::ostream &os_obj, Complex &c1);
    friend std::ostream &operator<<(std::ostream &os_obj, Complex &c1);
public:
    Complex(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void printCom()
    {
        std::cout << a << "+" << b << "i" << std::endl;
    }
};


/*
void operator<<(std::ostream &std::cout, Complex &c1)
{
    std::cout << c1.a << "+" << c1.b << "i" << std::endl;
}
*/

std::ostream &operator<<(std::ostream &os_obj, Complex &c1)
{
    os_obj << c1.a << "+" << c1.b << "i ";
    return os_obj;
}

int main()
{
    // 1.编译器按照已有数据类型输出
    int a = 10;
    std::cout << a << std::endl;

    // 2.对于自定义数据类型,需重载运算符输出
    // 这里用友元函数重载了<<运算符
    // 使得既能输出基础数据类型,也能输出自定义类型,也能支持链式编程
    Complex c1(1, 2);
    Complex c2(3, 4);

    std::cout << c1;
    std::cout << std::endl;

    std::cout << c1 << "hello world" << std::endl;
    std::cout << c1 << c2 << "xxxxxxx" << std::endl;

    return 0;
}

运行结果

10
1+2i 
1+2i hello world
1+2i 3+4i xxxxxxx

重载下标运算符[]

设 x 是类 X 的一个对象,则表达式x [ y ] 可被解释为 x . operator [ ] ( y )

示例代码

#include <iostream>

class Data
{
public:
    Data(int value = 0)
    {
        this->value = value;
    }
public:
    int value;    
};

class Test
{
public:
    Test(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
public:
    // 重载[],使得能传递整数类型
    int operator[](int index)
    {
        return a * index;
    }
    // 重载[],使得能传递Test类型
    int operator[](const Test &t)
    {
        return b * t.a;
    }
    // 重载[],使得能传递Data类型,且能链式编程
    Test &operator[](const Data &d)
    {
        std::cout << d.value << "\t";
        return *this;
    }
private:
    int a;
    int b;
};


int main()
{
    Test test(1, 2);

    // []传递整型
    std::cout << test[3] << std::endl;

    // []传递Test类型
    Test test2(3, 4);
    std::cout << test[test2] << std::endl;

    // []传递Data类型
    Data data1(3), data2(18), data3(99);
    test[data1][data2][data3];

    return 0;
}

运行结果

3
6
3	18	99

重载函数调用符()

设 x 是类 X 的一个对象,则表达式x ( arg1, arg2, … )可被解释为x . operator () (arg1, arg2, … )

#include <iostream>
#include <string>

// 类的声明
class F
{
public:
    double operator()(double x, double y);
    std::string operator()(std::string x, std::string y);
};

// 类的具体实现
double F::operator()(double x, double y)
{
    return x * x + y * y;
}

std::string F::operator()(std::string x, std::string y)
{
    std::string ret = "(";
    if (x.size() == 0)
        ret += " ";
    else
        ret += x[0];
    if (y.size() == 0)
        ret += " ";
    else
        ret += y[0];
    ret += ")";

    return ret;
}

int main()
{
    F f;
    std::cout << f(5.2, 2.5) << std::endl;; // 33.29
    std::cout << f.operator()(5.2, 2.5) << std::endl;; // 33.29

    std::cout << f("hello", "world") << std::endl;; // (hw)
    std::cout << f("", "xxxxx") << std::endl;; // ( x)

    return 0;
}

(6)单目运算符重载举例

重载右值++运算符

右值++运算符,++b,可被解释为operator ++ ( b )

示例代码

#include <iostream>
using namespace std;

class Complex
{
private:
    int a;
    int b;

public:
    Complex(int a, int b)
    {
        this->a = a;
        this->b = b;
    }

    void print_value()
    {
        cout << a << "+" << b << "i" << endl;
    }

public:
    // 右值++重载用成员函数实现
    Complex &operator++()
    {
        std::cout << "internal reload" << std::endl;
        this->a++;
        this->b++;
        return *this;
    }
    // 右值--重载用全局函数实现
    friend Complex &operator--(Complex &c);
};


Complex &operator--(Complex &c)
{
    std::cout << "external reload" << std::endl;
    c.a--;
    c.b--;
    return c;
}


int main()
{
    Complex c1(1, 2);
    Complex temp = ++c1; // internal reload
    c1.print_value();    // 2+3i
    temp.print_value();  // 2+3i

    --c1;   // external reload
    c1.print_value();    // 1+2i

    return 0;
}

运行结果

internal reload
2+3i
2+3i
external reload
1+2i

用双目运算符的思想重载左值++运算符

++作为单目运算符,直接重载的话,实现的是右值++,要重载左值++,技巧在于用一个占位符作为运算符重载函数的第二个参数,这样原本的变量就变成了左操作数。

示例代码

#include <iostream>

class Complex
{
private:
    int a;
    int b;
    // 重载右值++
    friend Complex &operator++(Complex &c1);
    // 重载左值++
    friend Complex operator++(Complex &c1, int);
public:
Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    void printCom()
    {
        std::cout << a << "+" << b << "i" << std::endl;
    }
public:
    // 重载右值--
    Complex &operator--()
    {
        this->a--;
        this->b--;
        return *this;
    }
    // 用占位符实现重载左值++
    Complex operator--(int)
    {
        Complex temp = *this;
        this->a -= 10;
        this->b -= 10;
        return temp;
    }
};

// 重载右值++
Complex &operator++(Complex &c1)
{
    c1.a++;
    c1.b++;
    return c1;
}

// 重载左值++
// 用占位符区分右值++和左值++
// 这里添加一个占位符,实现左值++重载
Complex operator++(Complex &c1, int)
{
    Complex temp = c1;
    c1.a += 10;
    c1.b += 10;
    return temp;
}

int main()
{
    Complex c1(1, 2);

    // 为了区分左值++和右值++正确被重载
    // 右值++的变化量为1,左值++的变化量为10

    // 重载左值++操作符
    Complex temp = c1++;
    //Complex temp = operator++(c1, 3);
    c1.printCom(); // 11+12i
    temp.printCom(); // 1+2i

    // 重载右值++操作符
    temp = ++c1;
    //temp = operator++(c1);
    c1.printCom(); // 12+13i
    temp.printCom(); // 12+13i


    // 重载左值--操作符
    temp = c1--;
    c1.printCom(); // 2+3i
    temp.printCom(); // 12+13i

    // 重载右值--操作符
    temp = --c1;
    c1.printCom(); // 1+2i
    temp.printCom(); // 1+2i


    return 0;
}

重载箭头->运算符

->一般用在指针当中。对它的重载稍微特殊一点,设 x 是类 X 的一个对象,则对于表达式x -> y :

  • 需要先看x ->部分,对应运算符重载函数x. operator ->(),如果该函数返回指针,比如返回p,则x ->就相当于返回p->,然后x -> y就相当于p->y,就是指针访问成员y,这里y可以是成员变量,比如p->a,也可以是成员函数,比如p->action()。
  • 如果函数x. operator ->()返回的不是指针,而是一个对象(比如m,假设m是类M的一个对象),则继续对该对象调用其重载了的箭头运算符(意味着类M也必须要对->进行重载,否则会报错),直到返回的是一个指针,假设为p,则最后就是在调用p->y。

示例代码

#include <iostream>
 
class firstClass 
{
public:
	firstClass* operator->() 
    {
		std::cout << "firstClass ->() is called!" << std::endl;
		return this;
	}
	void action() 
    {
		std::cout << "firstClass action() is called!" << std::endl;
		return;
	}
};
 
class myClass 
{
	firstClass firstObj;
public:
	firstClass& operator->() 
    {
		std::cout << "myClass ->() is called!" << std::endl;
		return firstObj;
	}
	void action() 
    {
		std::cout << "myClass action() is called!" << std::endl;
		return;
	}
};
 
int main() 
{
	myClass obj;
	obj->action();
 
	return 0;
}

运行结果

myClass ->() is called!
firstClass ->() is called!
firstClass action() is called!

(7)运算符重载步骤总结

运算符重载步骤

1)要承认操作符重载是一个函数,写出函数名称。

2)根据操作数,写出函数参数。

3)根据业务,完善函数返回值(看函数是返回引用 还是指针 还是元素),及实现函数业务。

4)根据情况考虑用全局函数还是类成员函数方法实现。

(8)为什么不建议重载&&和||操作符

&&和||是C++中非常特殊的操作符,&&和||内置实现了短路规则。

操作符重载是靠函数重载来完成的,操作数作为函数参数传递,C++的函数参数都会被求值,无法实现短路规则。

以下示例代码演示了可以实现&&的运算符的重载,但无法实现短路规则。

#include <iostream>
 
class Test
{
private:
    int i;
public:
    Test(int i = 0)
    {
        this->i = i;
    }
public:
    Test operator+(const Test &obj)
    {
        Test ret(0);
        std::cout << "+ reload func" << std::endl;
        ret.i = i + obj.i;
        return ret;
    }
    bool operator&&(const Test &obj)
    {
        std::cout << "&& reload func" << std::endl;
        return i && obj.i;
    }
};


int main()
{
    // 1.查看&&的短路规则
    int a1 = 0;
    int a2 = 1;

    // 注意:&&操作符的结合顺序是从左向右

    if (a1 && a2++) //有一个是假,则不在执行下一个表达式的计算
    {
        /* */
    }
    std::cout << a2 << std::endl; // 1

    // 2.重载&&后无法实现短路规则
    Test t1 = 0;
    Test t2 = 1;

    // if( t1 && (t1 + t2) )
    // t1 && t1.operator+(t2)
    // t1.operator&&( t1.operator+(t2) )

    // && || 重载他们 不会产生短路效果
    if (t1 && (t1 + t2)) { ; }
    // t1.operator+(t2) && t1;
    //(t1.operator+(t2)).operator&&(t1);

    // 两个函数都被执行了,而且是先执行了+
    // 说明没有实现短路规则

    return 0;
}

运行结果

1
+ reload func
&& reload func

(9)运算符重载在项目开发中的应用

自定义字符串类

用来练习各种运算符重载

构造函数要求

MyString a;

MyString a(“dddd”);

MyString b = a;

常用的操作符

> != == > < =

MyString.h

#include <iostream>
using namespace std;

//C中没有字符串,因此需要建一个字符串类
//C++中 我们来设计一个字符串 以零结尾的字符串
//空串 ""

class MyString
{
public:
    //用友元函数重载输入输出流
    friend ostream &operator<<(ostream &cout, MyString &s);
    friend istream &operator>>(istream &cin, MyString &s);
public:
    //构造函数和析构函数
    MyString(int len = 0); //传入整数构造
    MyString(const char *p); //传入字符串构造
    MyString(const MyString &s); //传入字符串类对象构造
    ~MyString();
    
public:
    //重载等号操作符 =
    MyString& operator=(const char *p);
    MyString& operator=(const MyString &s);
    //重载下标操作符 []
    public:
    char& operator[](int index);
    //重载 == != 操作符
    public:
    bool operator==(const char *p) const;
    bool operator==(const MyString &s) const;
    bool operator!=(const char *p) const;
    bool operator!=(const MyString &s) const;
    //重载 大于小于操作符
public:
    int operator<(const char *p) const;
    int operator>(const char *p) const;
    int operator<(const MyString &s) const;
    int operator>(const MyString &s) const;
public:
    //字符串类技巧:把指针露出来
    char *c_str()
    {
        return m_p;
    }
    const char*c_str2()
    {
        return m_p;
    }
    int getlen()
    {
        return m_len;
    }
private:
    int m_len;
    char *m_p;
};

MyString.cpp

#include <iostream>
using namespace std;
#include "MyString.h"
#include <cstring>

ostream &operator<<(ostream &cout, MyString &s)
{
    cout << s.m_p;
    return cout;
}

istream &operator>>(istream &cin, MyString &s)
{
    cin >> s.m_p;
    return cin;
}

// MyString::MyString(int len = 0) 
// 会error,‘MyString::MyString(int)’的第 1 个形参指定了默认实参
MyString::MyString(int len)
{
    if (len == 0)
    {
        m_len = len;
        m_p = new char[m_len + 1];
        strcpy(m_p, "");
    }
    else
    {
        m_len = len;
        m_p = new char[m_len + 1];
        memset(m_p, 0, m_len);
    }
}

MyString::MyString(const char *p)
{
    if (p == NULL)
    {
        m_len = 0;
        m_p = new char[m_len + 1];
        strcpy(m_p, "");
    }
    else
    {
        m_len = strlen(p);
        m_p = new char[m_len + 1];
        strcpy(m_p, p);
    }
}

//拷贝构造函数
// MyString s3 = s2;
MyString::MyString(const MyString &s)
{
    m_len = s.m_len;
    m_p = new char[m_len + 1];
    strcpy(m_p, s.m_p);
}

MyString::~MyString()
{
    if (m_p != NULL)
    {
        delete[] m_p;
        m_len = 0;
        m_p = NULL;
    }
}

// S4 = "hello world";
MyString &MyString::operator=(const char *p)
{
    // 1.先释放旧内存
    if (m_p != NULL)
    {
        delete[] m_p;
        m_len = 0;
    }
    // 2.根据p分配内存
    if (p == NULL)
    {
        m_len = 0;
        m_p = new char[m_len + 1];
        strcpy(m_p, "");
    }
    else
    {
        m_len = strlen(p);
        m_p = new char[m_len + 1];
        strcpy(m_p, p);
    }

    return *this;
}

// s4 = s2;
MyString &MyString::operator=(const MyString &s)
{
    // 1 旧内存释放掉
    if (m_p != NULL)
    {
        delete[] m_p;
        m_len = 0;
    }
    // 2 根据s分配内存
    m_len = s.m_len;
    m_p = new char[m_len + 1];
    strcpy(m_p, s.m_p);

    return *this;
}

char &MyString::operator[](int index)
{
    return m_p[index];
}

// (s2 == "hello world")
bool MyString::operator==(const char *p) const
{
    if (p == NULL)
    {
        if (m_len == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        if (m_len == strlen(p))
        {
            return !strcmp(m_p, p);
        }
        else
        {
            return false;
        }
    }
}

bool MyString::operator!=(const char *p) const
{
    return !(*this == p);
}

bool MyString::operator==(const MyString &s) const
{
    if (m_len != s.m_len)
    {
        return false;
    }
    return !strcmp(m_p, s.m_p);
}

bool MyString::operator!=(const MyString &s) const
{
    return !(*this == s);
}

// (s3 < "bbbb")
int MyString::operator<(const char *p) const
{
    return strcmp(this->m_p, p);
}

int MyString::operator>(const char *p) const
{
    return strcmp(p, this->m_p);
}

int MyString::operator<(const MyString &s) const
{
    return strcmp(this->m_p, s.m_p);
}

int MyString::operator>(const MyString &s) const
{
    return strcmp(s.m_p, m_p);
}

test.cpp

#include <iostream>
using namespace std;
#include <cstring>
#include "MyString.h"

int main01()
{
    MyString s1(1);
    MyString s2("s2");

    // MyString s2_2 = NULL; //这句会报错
    MyString s2_2 = "hello world";

    MyString s3 = s2;
    MyString s4 = "s4444444444";

    //测试运算符重载 = [] <<
    s4 = s2;
    cout << s4 << endl; // "s2"

    s4 = "s2222";
    s4[1] = '4';
    cout << s4 << endl; // "s4222"

    return 0;
}

int main02()
{
    MyString s1;
    MyString s2("s2");

    MyString s3 = s2;

    if (s2 == "aa")
        printf("equal\n");

    else
        printf("no equal\n");
    if (s3 == s2)
        printf("equal\n");
    else
        printf("no equal\n");

    return 0;
}

int main03()
{
    MyString s1;
    MyString s2("s2");

    MyString s3 = s2;
    s3 = "aaaa";

    int tag = (s3 < "bbb");
    if (tag < 0)
        printf("s3 less than bbb\n");
    else
        printf("s3 more than bbb\n");


    MyString s4 = "aaaaffff";
    strcpy(s4.c_str(), "aa111");
    cout << s4 << endl;

    return 0;
}

int main04()
{
    MyString s1(128);
    cout << "\n input content(Enter finish):"; // hello world
    cin >> s1;
    cout << s1; // hello

    //目前没有正确处理空格
    //可以完善

    return 0;
}

int main()
{
    // main01();

    // main02();

    // main03();

    main04();

    return 0;
}

自定义智能指针类

1问题抛出:指针使用过程中,经常会出现内存泄漏和内存多次被释放。

2 解决方案:例如:boost库的智能指针。项目开发中,要求开发者使用预先编写的智能指针类对象代替C语言中的原生指针。

3 智能指针思想

  • 工程中的智能指针是一个类模板
  • 通过构造函数接管申请的内存
  • 通过析构函数确保堆内存被及时释放
  • 通过重载指针运算符* 和 -> 来模拟指针的行为
  • 通过重载比较运算符 == 和 != 来模拟指针的比较

示例代码

#include <iostream>
using namespace std;

class Test
{
public:
    Test()
    {
        this->a = 10;
    }
    void printT()
    {
        cout << a << endl;
    }
private:
    int a;
};


class MyTestPointer
{
public:
public:
    MyTestPointer()
    {
        p = NULL;
    }
    MyTestPointer(Test *p)
    {
        this->p = p;
    }
    ~MyTestPointer()
    {
        delete p;
    }
    Test *operator->()
    {
        return p;
    }
    Test &operator*()
    {
        return *p;
    }

protected:
    Test *p;
};


class MyIntPointer
{
public:
public:
    MyIntPointer()
    {
        p = NULL;
    }
    MyIntPointer(int *p)
    {
        this->p = p;
    }
    ~MyIntPointer()
    {
        delete p;
    }
    int *operator->()
    {
        return p;
    }
    int &operator*()
    {
        return *p;
    }

protected:
    int *p;
};

void test1()
{
    Test *p = new Test;
    p->printT();
    delete p;
    MyTestPointer myp = new Test; //构造函数
    myp->printT(); //重载操作符 ->
};


void test2()
{
    int *p = new int(100);
    cout << *p << endl;
    delete p;
    MyIntPointer myp = new int(200);
    cout << *myp << endl; //重载*操作符
};


int main()
{
    test1();
    test2();
    
    return 0;
}

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/575951.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【opencv 加速推理】如何安装 支持cuda的opencv 包 用于截帧加速

要在支持CUDA的系统上安装OpenCV&#xff0c;您可以使用pip来安装支持CUDA的OpenCV版本。OpenCV支持CUDA加速&#xff0c;但需要安装额外的库&#xff0c;如cuDNN和NVIDIA CUDA Toolkit。以下是一般步骤&#xff1a; 安装NVIDIA CUDA Toolkit: 首先&#xff0c;您需要安装NVID…

qt5core.dll怎么下载,qt5core.dll丢失能否修复?

qt5core.dll的丢失真是让人头疼。这个Visual C Redistributable for Visual Studio 2015的运行时库被许多程序和游戏所依赖&#xff0c;一旦缺失了qt5core.dll&#xff0c;就会面临无法打开程序或游戏&#xff0c;甚至系统崩溃等一系列问题。 qt5core.dll的消失会带来以下麻烦 …

泰迪智能科技助力中山三院放射科搭建生成式大模型应用

泰迪智能科技作为一家专业从事物联网、大数据及人工智能技术研发、咨询与培训的高科技企业&#xff0c;具有强大的技术研发实力和应用经验。中山大学附属第三医院放射科是集医疗、教学、科研工作于一体的广东省临床重点专科&#xff0c;具有深厚的医疗资源和科研基础。两者合作…

GaN HEMT中短沟道效应的建模

来源&#xff1a;Modeling of Short-Channel Effects in GaN HEMTs&#xff08;TED 20年&#xff09; 摘要 在本文中&#xff0c;我们提出了一种用于估算GaN高电子迁移率晶体管&#xff08;HEMT&#xff09;器件中短沟道效应&#xff08;SCEs&#xff09;的显式和解析的基于电…

安卓和ios设置自己的短链

ios 的info.plist文件 设置 CFBundleURLSchemes 其中konnect 就是设置app的短链名称 <array><dict><key>CFBundleTypeRole</key><string>Editor</string><key>CFBundleURLName</key><string>org.konnect.app</str…

【Redis】Redis 非关系型数据库 安装、配置、使用(全集)

目录 Redis 第一章 1、什么是redis 2、安装redis 1-7 8 3、redis使用 第二章 1、redis的使用 1、使用方式 2、使用Java代码使用redis 3、优化连接redis 2、五种数据类型 常用命令 string hash list set zset 不同数据类型存、取、遍历的方法 3、redis在项目…

JCE cannot authenticate the provider BC

前言&#xff1a; 公司项目有用AES加密的&#xff0c;报错原因是BC&#xff08;Bouncy Castle&#xff09;提供的加密服务时&#xff0c;JCE&#xff08;Java Cryptography Extension&#xff09;无法进行验证。这通常是由于 JCE 的默认策略文件不支持所需的加密算法&#xff…

Windows下Golang初学乍到

安装 没啥说的&#xff0c;官网下载即可&#xff0c;地址&#xff1a;All releases - The Go Programming Language 根据系统类型下载即可&#xff01; 配置 Windows下安装完后&#xff0c;发现path中已经有了&#xff0c;但为了避免可能的问题&#xff0c;还是建议配置GOPA…

不得不说,在很多业务中,这种设计模式用得真的很香

故事 “不能在写if else来拓展当前系统了&#xff0c;现在已经有三个支付场景了…”工位上&#xff0c;小猫看着电脑&#xff0c;挠着头。 就在刚刚&#xff0c;小猫接到了一个新需求&#xff0c;需要和客户公司打通资产&#xff0c;形成资产联动。说白了就是需要定制化对接客…

Linux下基本指令-掌握

目录 为什么要学命令行 Linux下基本指令-掌握 ls 指令 pwd命令 cd 指令 touch指令 mkdir指令&#xff08;重要&#xff09;&#xff1a; rmdir指令 && rm 指令&#xff08;重要&#xff09;&#xff1a; man指令&#xff08;重要&#xff09;&#xff1a; cp指…

微信黑名单怎么恢复?一招迅速搞定

“求助&#xff01;微信拉黑后&#xff0c;怎样找到并解除黑名单&#xff1f;我不知道具体的操作&#xff0c;希望可以分析给我详细的图文解说&#xff0c;感谢&#xff01;” 微信的黑名单功能允许用户将某人加入黑名单&#xff0c;从而屏蔽其发送消息、查看朋友圈等行为。然…

7天入门Android开发之第1天——初识Android

一、Android系统 1.Linux内核层&#xff1a; 这是安卓系统的底层&#xff0c;它提供了基本的系统功能&#xff0c;如内存管理、进程管理、驱动程序模型等。安卓系统构建在Linux内核之上&#xff0c;借助于Linux的稳定性和安全性。 2.系统运行库层&#xff1a; 这一层包括了安卓…

最新windows版本erlang26.0和rabbitmq3.13下载

Erlang下载 官网下载&#xff1a;https://www.erlang.org/patches/otp-26.0 百度网盘&#xff1a;https://pan.baidu.com/s/1xU4syn14Bh7QR-skjm_hOg 提取码&#xff1a;az1t RabbtitMQ下载 官网下载&#xff1a;https://www.rabbitmq.com/docs/install-windows 百度网盘…

一文解读:阿里云 AI 基础设施的演进与挑战

云布道师 2024 年 4 月 18-19 日&#xff0c;2024 中国生成式 AI 大会在北京 JW 万豪酒店举行&#xff0c;阿里云高级技术专家、阿里云异构计算 AI 推理团队负责人李鹏受邀在【AI Infra】专场发表题为《AI 基础设施的演进与挑战》的主题演讲。李鹏从 AIGC 对云基础设施的挑战、…

echarts利用graphic属性给饼图添加内圈图片及外圈图片(可自适应宽度位于饼图中心)

最终效果图&#xff1a; 目录 前言资源主要部分graphic介绍style介绍代码 加载饼图方法&#xff08;option所有的配置&#xff09; 前言 思路是看到这个博客启发的&#xff1a;点击跳转查看博客&#xff0c;然后在graphic属性里改了我的实际需求&#xff0c;譬如图片的宽高、…

【笔试强训】除2!

登录—专业IT笔试面试备考平台_牛客网牛客网是互联网求职神器&#xff0c;C、Java、前端、产品、运营技能学习/备考/求职题库&#xff0c;在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://ac.nowcoder.com/acm/…

如何安装sbt(sbt在ubuntu上的安装与配置)(有详细安装网站和图解)

sbt下载官网 选择对应的版本和安装程序 Download | sbt (scala-sbt.org) 安装 解压 将sbt-1.9.0.tgz上传到xshell&#xff0c;并解压 解压&#xff1a; tar -zxvf sbt-1.9.0.tgz 配置 1、在/home/hadoop/sbt中创建sbt脚本 /home/hadoop/sbt 注意要改成自己的地址 cd …

16 JavaScript学习: 类型转换

JavaScript 类型转换 Number() 转换为数字&#xff0c; String() 转换为字符串&#xff0c; Boolean() 转换为布尔值。 JavaScript 数据类型 在 JavaScript 中有 6 种不同的数据类型&#xff1a; stringnumberbooleanobjectfunctionsymbol 3 种对象类型&#xff1a; Obje…

Springboot多数据源及事务实现方案

Springboot多数据源及事务实现方案 文章目录 Springboot多数据源及事务实现方案背景问题分析实现原理1. 数据源抽象与动态路由2. 线程本地存储&#xff08;ThreadLocal&#xff09;3. 面向切面编程&#xff08;AOP&#xff09;4. 自定义注解 实现流程1. 设置数据源标识2. 开始数…

Godot3D学习笔记1——界面布局简介

创建完成项目之后可以看到如下界面&#xff1a; Godot引擎也是场景式编程&#xff0c;这里的一个场景相当于一个关卡。 这里我们点击左侧“3D场景”按钮创建一个3D场景&#xff0c;现在在中间的画面中会出现一个球。在左侧节点视图中选中“Node3D”&#xff0c;右键创建子节点…