C++运算符重载

news2025/7/11 23:48:23

运算符重载

  • 运算符重载
    • 一、重载‘+’运算符
      • 1.非成员函数版本重载
      • 2.成员函数版本
    • 二、重载关系运算符
    • 三、重载左移运算符
    • 四、重载下标运算符
    • 五、重载赋值运算符
    • 六、重载new和delete
      • 1.重载
      • 2.内存池
    • 七、重载括号运算符
    • 八、重载一元运算符

运算符重载

C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。
例如字符串string用加号(+)拼接,cout用两个左尖括号(<<)输出。

语法:返回值 operator 运算符(参数列表)
非成员函数版本的重载运算符函数:形参个数与运算符操作数个数相同;
成员函数版本的重载运算符函数:形参个数比运算符的操作数个数少一个,其中一个操作数隐式传递了调用对象。

一、重载‘+’运算符

1.非成员函数版本重载

#include<iostream>
using namespace std;
class CStudent
{
    public:
    int score;
    int ID;
    CStudent(){score=80;ID=0;}
    void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
};
void operator+(CStudent &s,int a)
{
    s.score=s.score+a;
}
int main()
{
    CStudent s;
    operator+(s,1);
    s.show();
    s+2;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述

空返回值的运算符重载有个弊端,就是在同一条语句下不能连续使用,如:
在这里插入图片描述
如果需要在同一条语句中连续使用,返回值类型不能为空。如下所示:

#include<iostream>
using namespace std;
class CStudent
{
    public:
    int score;
    int ID;
    CStudent(){score=80;ID=0;}
    void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
};
CStudent& operator+(CStudent &s,int a)//返回值类型不为空
{
    s.score=s.score+a;
    return s;
}
int main()
{
    CStudent s;
    operator+(s,1);
    s.show();
    s+2+3 ;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述

2.成员函数版本

看下面的例子:
在这里插入图片描述
说明:成员函数运算符重载里只能有一个参数。

对于+号运算符来说,类外运算符重载和运算符重载同时存在时,系统会不知道调用哪一个,所以要去掉类外运算符重载。
在这里插入图片描述


#include<iostream>
using namespace std;
class CStudent
{
    public:
    int score;
    int ID;
    CStudent(){score=80;ID=0;}
    void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
    void operator+(int b){this->score=this->score+b;}
};
// CStudent& operator+(CStudent &s,int a)
// {
//     s.score=s.score+a;
//     return s;
// }
int main()
{
    CStudent s;
    s.operator+(1);
    s.show();
    s+5;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述

同理,返回值类型为void 时,运算符在同一条语句中不能连用,需将返回值返回一个对象引用

#include<iostream>
using namespace std;
class CStudent
{
    public:
    int score;
    int ID;
    CStudent(){score=80;ID=0;}
    void show(){cout<<"ID:"<<ID<<endl;cout<<"分数:"<<score<<endl;}
    CStudent& operator+(int b){this->score=this->score+b;return *this;}
};
int main()
{
    CStudent s;
    (s.operator+(1)).operator+(2);
    s.show();
    s+5+2;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述

注意事项:
返回自定义数据类型的引用可以让多个运算符表达式串联起来。(不要返回局部变量的引用)
重载函数参数列表中的顺序决定了操作数的位置。
重载函数的参数列表中至少有一个是用户自定义的类型,防止程序员为内置数据类型重载运算符。
如果运算符重载既可以是成员函数也可以是全局函数,应该优先考虑成员函数,这样更符合运算符重载的初衷。
重载函数不能违背运算符原来的含义和优先级。
不能创建新的运算符。

以下运算符只通过成员函数进行重载:
= 赋值运算符
()函数调用运算符
[]下标运算符
->通过指针访问类成员的运算符

二、重载关系运算符

关系运算符有:==、!=、>、>=、<、<=用于比较两个自定义数据类型的大小。重载关系运算符时尽量使用成员函数版本。

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {}
    bool operator==(CStudent &rhs)
    {
        if((this->chinese+this->math+this->english)==(rhs.chinese+rhs.english+rhs.math))
        return true;
        return false;
    }
};
int main()
{
    CStudent s1("李华",85,92,95);
    CStudent s2("李明",82,94,96);
    if(s1==s2)
    {
        cout<<"李华和李明成绩一样!"<<endl;
    }
    else
    cout<<"李华和李明成绩不一样!"<<endl;
    return 0;
}

执行结果:
在这里插入图片描述

三、重载左移运算符

重载左移运算符(<<)用于输出自定义对象的成员变量,在实际开发中很有价值(调试和日志) 只能使用非成员函数版本。
如果输出对象的私有成员,可以配合友元一起使用。

语法:

ostream& operator<<(ostream& cout,constant type& rhs)
{
cout<<rhs.xxx<<……<<endl;
return cout;
}

示例:

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {}
    
};
ostream &operator<<(ostream& cout,const CStudent&s)
{
    cout<<"姓名:"<<s.name<<"\n语文成绩:"<<s.chinese<<"\n数学成绩:"<<s.math<<"\n英语成绩:"<<s.english<<endl;
    return cout;
}
int main()
{
   CStudent s("小明",88,79,92);
   cout<<s;
   return 0;
} 

运行结果:
在这里插入图片描述

四、重载下标运算符

如果对象中有数组,重载下标运算符[],操作对象中的数组将像操作普通数组一样方便。
下标运算符必须以成员函数的形式进行重载。

语法:
返回值类型 &operator 或者:const 返回值类型 &operator[](参数)const
使用第一种声明方式,[]不公可以访问数组元素,还可以修改数组元素。 使用第二种声明方式,[]只能访问不能修改数组元素。
在实际开发中,两种都要提供,目的是为了迁就常对象,因为常对象不能访问非常函数。

看下面的例子:

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)
    {
        return this->friends[i];
    }
    
};

int main()
{
   const CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
结论:常对象不能访问非常成员函数

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)
    {
        return this->friends[i];
    }
    
};

int main()
{
   CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述


#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
     string &operator[](int i)
    {
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   s[2]="小北";
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述> 说明:可以通过重载下标运算符来访问和修改成员变量。

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    const string &operator[](int i)//表示返回值不允许修改
    {
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   s[2]="小北";
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
结论:重载下标运算符的返回值为常引用时,不允许修改成员变量。

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)const//常成员函数,注意const的位置。
    {
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   s[2]="小北";
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
结论:常成员函数不允许访问非常成员变量。

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    const string &operator[](int i)//表示返回值不允许修改,但重载函数内部可以修改
    {
        this->friends[i]="小豪";
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    const string &operator[](int i)const//表示返回值不允许修改,重载函数内部也不可以修改
    {
        this->friends[i]="小豪";
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    const string &operator[](int i)const
    {
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
结论:非常对象可以访问常成员函数


#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)const
    {
        return this->friends[i];
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
没有首const 不能访问非常成员变量

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    const string &operator[](int i)
    {
        return this->friends[i];
    }
};

int main()
{
   const CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
常对象不能访问非常成员函数

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)
    {
        return this->friends[i];
    }
    const string &operator[](int i)const 
    {
        return this->friends[i];
    }
};

int main()
{
   const CStudent s("小明",88,79,92);
   cout<<s[2]<<endl;
   CStudent s1("小叶",92,85,93);
   s1[2]="小明";
   cout<<s1[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    string &operator[](int i)
    {
        return this->friends[i];
    }
    const string &operator[](int i)const 
    {
        return this->friends[i+1];
    }
};

int main()
{
   const CStudent s("小明",88,79,92);
   cout<<s[1]<<endl;
   CStudent s1("小叶",92,85,93);
   cout<<s1[1]<<endl;
   return 0;
}

执行结果:
在这里插入图片描述
说明:非常对象优先调用非常成员函数重载,常对象优先调用常成员函数重载。

五、重载赋值运算符

C++编译器可能会给类型添加四个函数:
默认构造函数,空实现。
默认析构函数,空实现。
默认挎贝构造函数,对成员变量进行浅挎贝。
默认赋值函数,对成员变量进行浅挎贝。
对象的赋值运算是用一个已经存在的对象,给另一个已经存在的对象赋值。
如果类的定义中没有重载赋值函数,编译器就会提供一个默认的赋值函数。
如果类中重载了赋值函数,编译器将不提供默认赋值函数。

请看下面的例子:

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s1=s;
   cout<<s1.chinese<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
定义了带参构造函数,默认的空无参构造函数失效。

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    CStudent(){}
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s1=s;
   cout<<s1.chinese<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
说明:上述代码对构造函数进行了重载

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    CStudent(){}
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s1=s;
   cout<<s1.friends[1]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
说明:数组也能赋值

对数组来说,这是一种深挎贝:

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   cout<<s.friends<<endl;
   s1=s;
   cout<<s1.friends<<endl;
   cout<<s1.friends[1]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

重载赋值函数语法:类名 & operator=(const 类名 & 源对象)

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    CStudent(){}
    CStudent &operator=(const CStudent& s)
    {
        if(this==&s)return *this;
        this->name=s.name;
        this->english=s.english;
        this->chinese=s.chinese;
        this->math=s.math;
        for(int i=0;i<3;++i)
        {
            this->friends[i]=s.friends[i];
        }
        return *this;
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s1=s;
   cout<<s1.name<<endl;
   cout<<s1.math<<endl;
   cout<<s1.friends[2]<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

编译器默认的赋值函数是浅挎贝。如果对象中不存在堆区内存空间,默认赋值函数可以满足需求,否则需要深挎贝。
赋值运算和挎贝构造不同:拷贝构造是指对象不存在,用已存在的对象进行构造;赋值运算是指已经存在了两个对象,把其中一个对象的成员变量的值赋给另一个对象的成员变量。

#include<iostream>
#include<memory.h>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    int* m_ptr;//计划使用堆区内存
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
        m_ptr=nullptr;
    }
    CStudent(){}
    CStudent &operator=(const CStudent& s)
    {
        if(this==&s)return *this;
        this->name=s.name;
        this->english=s.english;
        this->chinese=s.chinese;
        this->math=s.math;
        for(int i=0;i<3;++i)
        {
            this->friends[i]=s.friends[i];
        }
        if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
        {
            if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
        }
        else //如果源对象的指针不为空
        {
            //如果目标对象的指针为空,先分配内存
            if(m_ptr==nullptr)m_ptr=new int;
            //然后,把源对象内存中的数据复制到目标对象的内存中
            memcpy(m_ptr,s.m_ptr,sizeof(int));
        }
        return *this;
    }
    ~CStudent(){
        delete m_ptr;
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   cout<<s.m_ptr<<endl;
   s1.m_ptr=new int(3);
   cout<<s1.m_ptr<<endl;
   s1=s;
   cout<<s1.m_ptr<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述

如果对象中存在堆内存空间,一定要在构造函数中初始化对象,否则会产生意想不到的异常。如下:

#include<iostream>
#include<memory.h>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    int* m_ptr;//计划使用堆区内存
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        cout<<"调用带参构造函数"<<endl;
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
        m_ptr=nullptr;
        
    }
    CStudent(){
        cout<<"调用无参构造函数"<<endl;
       // m_ptr=nullptr;//没有进行初始化
    }
    CStudent &operator=(const CStudent& s)
    {
        if(this==&s)return *this;
        this->name=s.name;
        this->english=s.english;
        this->chinese=s.chinese;
        this->math=s.math;
        for(int i=0;i<3;++i)
        {
            this->friends[i]=s.friends[i];
        }
        if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
        {
            if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
        }
        else //如果源对象的指针不为空
        {
            //如果目标对象的指针为空,先分配内存
            if(m_ptr==nullptr)m_ptr=new int;
            //然后,把源对象内存中的数据复制到目标对象的内存中
            memcpy(m_ptr,s.m_ptr,sizeof(int));
        }
        return *this;
    }
    ~CStudent(){
        delete m_ptr;
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s.m_ptr=new int(3);
   s1=s;
  cout<<s.m_ptr<<endl;
  cout<<s1.m_ptr<<endl;
  cout<<*(s1.m_ptr)<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
说明:产生了异常

改进:


#include<iostream>
#include<memory.h>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    int* m_ptr;//计划使用堆区内存
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        cout<<"调用带参构造函数"<<endl;
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
        m_ptr=nullptr;
        
    }
    CStudent(){
        cout<<"调用无参构造函数"<<endl;
        m_ptr=nullptr;
    }
    CStudent &operator=(const CStudent& s)
    {
        if(this==&s)return *this;
        this->name=s.name;
        this->english=s.english;
        this->chinese=s.chinese;
        this->math=s.math;
        for(int i=0;i<3;++i)
        {
            this->friends[i]=s.friends[i];
        }
        if(s.m_ptr==nullptr)//如果源对象的指针为空,则清空目标对象的内存和指针
        {
            if(m_ptr!=nullptr){delete m_ptr;m_ptr=nullptr;}
        }
        else //如果源对象的指针不为空
        {
            //如果目标对象的指针为空,先分配内存
            if(m_ptr==nullptr)m_ptr=new int;
            //然后,把源对象内存中的数据复制到目标对象的内存中
            memcpy(m_ptr,s.m_ptr,sizeof(int));
        }
        return *this;
    }
    ~CStudent(){
        delete m_ptr;
    }
};

int main()
{
   CStudent s("小明",88,79,92);
   CStudent s1;
   s.m_ptr=new int(3);
   s1=s;
  cout<<s.m_ptr<<endl;
  cout<<s1.m_ptr<<endl;
  cout<<*(s1.m_ptr)<<endl;
   return 0;
} 

执行结果:
在这里插入图片描述
说明:在构造函数中初始化对象后就没问题了。

六、重载new和delete

1.重载

重载new和delete运算符的目的是为了自定义内存分配的细节。(内存池:快速分配和归还,无碎片)
在c++中,使用new时,编译器做了两件事:
1)调用标准库函数operator new()分配内存;
2)调用构造函数初始化内存;
使用delete时,也做了两件事情:
1)调用析构函数;
2)调用标准库函数operator delete()释放内存。
构造函数和析构函数由编译器的调用,我们无法控制。但是可以重载内存分配函数operator new()和释放函数operator delete();
1.重载内存分配函数语法:void* operator new(size_t size);
参数必须是size_t,返回值必须是void*.
2.重载内存释放函数的语法:void operator delete(void* ptr)
参数必须是void (指向由operator new()分配的内存),返回值必须是void .
重载的new和delete时,尽管不必显式地使用static,但实际上仍在创建static成员函数。
void
表示该指针指向的是一块与类型无关的内存空间

下面演示为整型变量动态分配内存:

#include<iostream>
using namespace std;
void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}
int main()
{
    int *p1=new int(3);
    cout<<"p1="<<(void*)p1<<", *p1="<<*p1<<endl;
    delete p1;
    return 0;
}

执行结果:
在这里插入图片描述

下面演示为类动态分配内存:

#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    string friends[3];
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
        friends[0]="小华";
        friends[1]="小红";
        friends[2]="小方";
    }
    
};

void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}
int main()
{
    CStudent *p2=new CStudent("小明",89,85,86);
    cout<<"p2的地址是:"<<p2<<"姓名:"<<p2->name<<",朋友2:"<<p2->friends[1]<<endl;
    delete p2;
    return 0;
}

执行结果:
在这里插入图片描述
说明:调用了那么多次有可能是使用了string的原因。
以下来验证(删除string数组):




#include<iostream>
using namespace std;
class CStudent{
    public:
    string name;//这个name是一个指针(8个字节)
    int chinese;
    int math;
    int english;
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
    }
    
};

void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}
int main()
{
    CStudent *p2=new CStudent("小明",89,85,86);
    cout<<"p2的地址是:"<<p2<<"姓名:"<<p2->name<<endl;
    delete p2;
    return 0;
}

执行结果:
在这里插入图片描述
说明:的确的string的原因。

int test[5];
cout<<sizeof(p2->test)<<endl;

执行结果:
在这里插入图片描述
说明:对于像数组这样的静态指针来说,sizeof是可以识别出它所指向的内存大小的。

#include<iostream>
#include<vector>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    vector<int>test{1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
    }
    
};

void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}
int main()
{
    CStudent *p2=new CStudent("小明小方小蓝",89,85,86);
    cout<<"p2的地址是:"<<p2<<endl<<"姓名:"<<p2->name<<endl;
    cout<<sizeof(*p2)<<endl;
    delete p2;
    return 0;
}

执行结果:
在这里插入图片描述
说明:STL变量的本质就是一个指针,虽然不是动态的,但sizeof 无法识别。

#include<iostream>
#include<vector>
using namespace std;
class CStudent{
    public:
    string name;
    int chinese;
    int math;
    int english;
    vector<int>test{1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
    }
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了类的重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了类的重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}
    
};

    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    cout<<"调用了全局重载的new:"<<size<<"字节。\n";
    void* ptr=malloc(size);//申请内存
    cout<<"申请到的内存的地址是:"<<ptr<<endl;
    return ptr;
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    cout<<"调用了全局重载的delete.\n";
    if(ptr==0)return;//对空指针delete是安全的
    free(ptr);//释放内存
}


int main()
{
    CStudent *p2=new CStudent("小明小方小蓝",89,85,86);
    cout<<"p2的地址是:"<<p2<<endl<<"姓名:"<<p2->name<<endl;
    cout<<sizeof(*p2)<<endl;
    delete p2;
    return 0;
}

执行结果:
在这里插入图片描述
说明:string 用的是全局new
CStudent用的是类的new

为一个类重载New和delete时,尽管不必显式地使用static,但实际上仍在创建static成员函数。
编译器看到使用new创建自定义类的对象时,它选择成员版本的operator new()而不是全局版的new().
new[]和delete[]也可以重载。

2.内存池

预先分配一大块内存空间
提升分配和归还速度
减少内存碎片

#include<iostream>
#include<memory.h>
#include<vector>
using namespace std;
class CStudent{
public:
    string name;
    int chinese;
    int math;
    int english;
    static char* m_pool;//内存池的起始地址
    static bool initpool()//初始化内存池的函数
    {
        m_pool=(char*)malloc(50);
        if(m_pool==0)return false;//如果申请失败,返回false
        memset(m_pool,0,50);//把内存池中的内容初始化为0
        cout<<"内存池的起始地址是:"<<(void*)m_pool<<endl;
        return true;
    }
    static void freepool()
    {
        if(m_pool==0)return ;//如果内存池为空,不需要释放,直接返回
        free(m_pool);//把内存池归还给系统
        cout<<"内存池已释放。\n"<<endl;
    }
    CStudent(string s_name,int s_chinese, int s_math, int m_english):
    name(s_name),chinese(s_chinese),math(s_math),english(m_english)
    {
    }
    void* operator new(size_t size)//参数必须是size_t(unsigned long long),返回值必须是void*.
{
    if(m_pool[0]==0)//判断第一个位置是否空闲
    {
        cout<<"分配了第一块内存:"<<(void*)(m_pool+1)<<endl;
        m_pool[0]=1;//把第一个位置标记为已分配
        return m_pool+1;//返回第一个存放对象的地址
    }
    if(m_pool[25]==0)
    {
        cout<<"分配了第二块内存:"<<(void*)(m_pool+1)<<endl;
        m_pool[25]=1;//把第一个位置标记为已分配
        return m_pool+26;//返回第一个存放对象的地址
    }
    //如果以上两位置都不可用,那主直接系统申请内存
    void *ptr=malloc(size);//申请内存
    cout<<"申请到的内存地址是:"<<ptr<<endl;
    return ptr;
    
}
void operator delete(void* ptr)//参数必须是void*,返回值必须是void.
{
    if(ptr==0)return;//如果传进来的地址为空 ,直接返回
    if(ptr==m_pool+1)//如果传进来的地址是内存池的第一个位置
    {
        cout<<"释放第一块内存。\n";
        m_pool[0]=0;//把第一个位置标记为空闲
        return ;
    }
    if(ptr==m_pool+26)//如果传进来的地址是内存池的第二个位置
    {
        cout<<"释放第二块内存。\n";
        m_pool[25]=0;
        return ;
    }
    //如果传进来的地址不属于内存池,把它归还给系统
    cout<<"释放其他内存块。\n"<<endl;
    free(ptr);
}
};
char* CStudent::m_pool=0;//初始化内存池的指针
int main()
{
    //初始化内存池 
    if(CStudent::initpool()==false){cout<<"初始化内存池失败。\n";return -1;}
    CStudent *p1=new CStudent("小方",89,85,86);
    cout<<"sizeof(*p1):"<<sizeof(*p1)<<endl;
    CStudent *p2=new CStudent("小方",89,85,86);
    cout<<"sizeof(*p2):"<<sizeof(*p2)<<endl;
    CStudent *p3=new CStudent("小方",89,85,86);
    cout<<"sizeof(*p3):"<<sizeof(*p3)<<endl;
    delete p1;
    delete p2;
    delete p3;
    CStudent::freepool();
    return 0;
}

执行结果:
在这里插入图片描述
上面只是演示,这个内存池是有一堆bug的

七、重载括号运算符

括号运算符也可以重载,对象名可以当成函数来使用(函数对象、仿函数)。
括号运算符重载函数的语法:
返回值类型 operator()(参数列表)
注意:
括号运算符必须以成员函数的形式进行重载。
括号运算符重载函数具备普通函数全部的特征。
如函数对象与全局函数同名,按作用域规则选择调用的函数

请看下面例子:

#include<iostream>
using namespace std;
void show(string str)
{
    cout<<"普通函数:"<<str<<endl;
}
class CStudent
{
    public:
    void operator()(string str)//重载()运算符
    {
        cout<<"重载函数:"<<str<<endl;
    }
};
int main()
{
    CStudent s;
    s("我是一只小菜鸟。");//使用重载的括号运算符
    show("我是一只小菜鸟。");
    return 0;
}

执行结果:
在这里插入图片描述

#include<iostream>
using namespace std;
void show(string str)
{
    cout<<"普通函数:"<<str<<endl;
}
class CStudent
{
    public:
    void operator()(string str)//重载()运算符
    {
        cout<<"重载函数:"<<str<<endl;
    }
};
int main()
{
    CStudent show;
    show("我是一只小菜鸟。");//使用重载的括号运算符
    show("我是一只小菜鸟。");
    return 0;
}

执行结果:
在这里插入图片描述
当普通函数和重载函数同时出现时,优先调用重载函数。
如何解决这个问题?
使用::,表示使用的是全局函数

#include<iostream>
using namespace std;
void show(string str)
{
    cout<<"普通函数:"<<str<<endl;
}
class CStudent
{
    public:
    void operator()(string str)//重载()运算符
    {
        cout<<"重载函数:"<<str<<endl;
    }
};
int main()
{
    CStudent show;
    show("我是一只小菜鸟。");//使用重载的括号运算符
    ::show("我是一只小菜鸟。");
    return 0;
}

执行结果:
在这里插入图片描述

函数对象的用途:
1)表面像函数,部分场景中可以代替函数,在STL中得到广泛的应用;
2)函数对象的本质是类,可以用成员变量存放更多的信息;
3)函数对象有自己的数据类型;
4)可以提供继承体系。

八、重载一元运算符

下表为可重载的一元运算符:
在这里插入图片描述

一元运算符通常出现在它们所操作的对象的左边。
但是,自增运算符++和自减运算符–有前置和后置之分。
为了解决这个问题,C++规定,在重载++或–时,允许写一个增加了int形参的版本,编译器处理后置表达式时,

以下代码编译会报错:

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    void operator++()
    {
        m_ranking++;
    }
};
int main()
{
    CStudent s;
    s++;
    return 0;
}

这里是引用
说明:因为没有重载类的后置++

再看下面的代码:


#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    void operator++()
    {
        m_ranking++;
    }
};
int main()
{
    CStudent s;
    ++s;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述
但是上面的代码不够完美,因为不可以连续前置++

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    void operator++()
    {
        m_ranking++;
    }
};
int main()
{
    CStudent s;
    ++(++s);
    s.show();
    return 0;
}

这里是引用

如何解决?返回对象的引用:

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    CStudent& operator++()
    {
        m_ranking++;
    }
};
int main()
{
    CStudent s;
    ++(++s);
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述

如何重载后置++?

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    CStudent& operator++()
    {
        m_ranking++;
        return *this;
    }
    CStudent& operator++(int)//int形参
    {
        m_ranking++;
        return *this;
    }
};
int main()
{
    CStudent s;
    s++;
    s.show();
    return 0;
}

执行结果:
在这里插入图片描述
上面的代码也是有问题的

请看下面的例子:
在这里插入图片描述
标准的c++不支持后置++连续使用

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    CStudent& operator++()
    {
        m_ranking++;
        return *this;
    }
    CStudent& operator++(int)
    {
        m_ranking++;
        return *this;
    }
};
int main()
{
    CStudent s1,s2;
    int ii=5,jj=5;
    int xx=++(++(++ii));cout<<"xx="<<xx<<",ii="<<ii<<endl;
    int yy=jj++;;cout<<"yy="<<yy<<",jj="<<jj<<endl;
    ((s2++)++)++;
    s2.show();
    return 0;
}

执行结果:
在这里插入图片描述
然而上面的定义却支持嵌套后置++,虽然支持,但最好别用

再看下面的例子:

#include<iostream>
using namespace std;
class CStudent
{
    public:
    string m_name;
    int m_ranking;//排名
    //默认构造函数
    CStudent(){m_name="小明";m_ranking=5;}
    //自我介绍的方法
    void show()const{cout<<"姓名:"<<m_name<<",排名:"<<m_ranking<<endl;}
    CStudent& operator++()
    {
        m_ranking++;
        return *this;
    }
    CStudent& operator++(int)
    {
        m_ranking++;
        return *this;
    }
};
int main()
{
    CStudent s1,s2;
    int ii=5,jj=5;
    int xx=++(++(++ii));cout<<"xx="<<xx<<",ii="<<ii<<endl;
    int yy=jj++;;cout<<"yy="<<yy<<",jj="<<jj<<endl;
    CStudent s3=++(++(++s1));cout<<"s3.m_ranking="<<s3.m_ranking<<",s1.m_ranking="<<s1.m_ranking<<endl;
    CStudent s4=s2++;cout<<"s4.m_ranking="<<s4.m_ranking<<",s2.m_ranking="<<s2.m_ranking<<endl;
    return 0;
}

执行结果:
在这里插入图片描述
说明:显然类重载后的后置++运算符与整数运算的效果不一样,这个结果不符合++运算符的后置语义。

改进方法:

 CStudent operator++(int)
    {
        CStudent tmp=*this;
        m_ranking++;
        return tmp;
    }

执行结果:
在这里插入图片描述

但注意函数的返回值不能是引用,否则:

CStudent &operator++(int)
    {
        CStudent tmp=*this;
        m_ranking++;
        return tmp;
    }

执行结果:
在这里插入图片描述
总结:
在这里插入图片描述

学习链接

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

23 张图细讲使用 Devtron 简化 K8S 中应用开发

23 张图细讲使用 Devtron 简化 K8S 中应用开发 在本文中&#xff0c;您将学习如何在多集群环境中使用 Devtron 在 K8S 上进行应用开发。 https://devtron.ai/ Devtron 附带用于构建、部署和管理微服务的工具。它通过提供直观的 UI 和 Helm 图表支持来简化 K8S 上的部署。今天&a…

Java数据结构 | 模拟实现优先级队列

目录 一、前言 二、堆模拟实现优先级队列 2.1 堆的概念 2.2 堆的性质 2.3 堆的存储方式 2.4 堆的创建 一、前言 在前面我们学习过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队…

mybatis-plus代码生成工具

mybatis-plus版本升级尝试遇到的问题 若遇到高版本&#xff1a;【全局覆盖已有文件的配置已失效&#xff0c;已迁移到策略配置中】or【覆盖已有文件&#xff08;已迁移到策略配置中&#xff0c;3.5.4版本会删除此方法&#xff09;】这句话&#xff0c;可参考文章中解决办法 参考…

python机器人编程——基于单目视觉、固定场景下的自动泊车(下)

目录一、前言二、主要思路step0 设定一个中间位置step1 掉转马头step2 直线匀速前进step3 调整姿态step4 视觉匹配三、效果四、全篇总结一、前言 本篇来讨论一下在固定场景下&#xff0c;如何仅通过单目视觉&#xff0c;实现差速小车的自动停靠&#xff0c;这种方式实现成本比…

_cpp 位图

文章目录1. 位图概念1.1 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。2. 位图的实现2.1 运行结果&#xff1a;3. 位图应用3.1 具体代码封装实现如下3.2 部分结果演示&#xff1a;3.3 应用总结&#x…

力扣(LeetCode)791. 自定义字符串排序(C++)

排序 这道题只关心 orderorderorder 出现的字符&#xff0c;在 sss 中的排序。 sss 中不在 orderorderorder 的字符&#xff0c;在排序后是什么位置&#xff0c;不影响答案。 可以用 sortsortsort 函数&#xff0c;传入我们自定义的排序方式&#xff0c;按照 orderorderorder …

【JavaSE】类和对象 (二) —— 封装、包以及 static 关键字

目录 1. 封装 1.1 封装的概念 1.2 访问限定符 2. 包 2.1 包的概念 2.2 导入包中的类 2.3 自定义包 2.3.1 包的基本规则 2.3.2 创建一个包 2.4 包访问权限——defualt 3. 何为封装 3.1 private 关键字 4. static 成员 4.1 对学生类的进一步思考 4.2 static 修饰成员变量 4.3 …

计算机毕业设计之java+javaweb的物业管理系统

项目介绍 系统权限按管理员,物业和住户这三类涉及用户划分。 (a) 管理员&#xff1a;管理员使用本系统涉到的功能主要有&#xff1a;首页,个人中心,用户管理,员工管理,房屋类型管理,房源信息管理,房屋预约管理,订单信息管理,我的收藏管理,系统管理等功能。 (b) 住户&#xf…

基于单片机的导盲拐杖设计

目 录 引言 1 1 系统概述 1 1.1 设计研究的背景和意义 1 1.2 本次设计内容 1 2 系统设计的整体方案 2 2.1 主控芯片的方案论证 2 2.2 显示模块的方案论证 3 2.3 本章小节 4 3 系统硬件电路设计 4 3.1 单片机最小系统的电路设计 4 3.1.1 STC…

李峋同款爱心代码!跳动的心,给你爱的人一个惊喜!

Hello 大家好 如何浪漫的表白&#xff0c;作为程序员出身的小编&#xff0c;今天就带你实现热播剧《点燃我&#xff0c;温暖你》中超火的李峋同款爱心代码&#xff01;前面是教程&#xff0c;怕麻烦的朋友可以直接划到文末&#xff0c;下载现成的&#xff0c;下载完成后打开就可…

java毕业设计基于的校园头条新闻管理系统的设计与实现(附源码、数据库)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

【Linux】基础:进程地址空间

【Linux】基础&#xff1a;进程地址空间 摘要&#xff1a;本文首先通过复习关于C语言内存空间的知识来做实验提出问题&#xff0c;从而引入进程的地址空间。需要理解的是进程地址空间的组织形式与其表示意义&#xff0c;在需要理解如何完成进程地址空间的划分以及关键对应物理内…

C++12 ---对象于对象的关系

一、对象于对象的关系 在一个系统中&#xff0c;一个对象可能与不同的对象相关&#xff0c;以下是不同的关系。 依赖(Dependency) (使用一个) 关联(Association) (使用一个) 聚合(Aggregation) (有一个) 组合(Composition ) (有一个&#xff0c;"用..来实现") …

从Matlab实例学习遗传算法

文章目录前言问题背景遗传算法Matlab实例代码附录君主方案遗传算法解决旅行商问题前言 本文旨在使用智能优化算法及其MATLAB实例&#xff08;第2版&#xff09; 一书中的例子&#xff0c;来透彻理解遗传算法的本质。 问题背景 目标&#xff1a; 求解最大化函数 f(x)x10sin⁡…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java星光之夜香水网站的设计与开发bfmcr

大学计算机专业毕业的&#xff0c;实际上到了毕业的时候&#xff0c;基本属于会与不会之间。说会&#xff0c;是因为学了整套的理论和方法&#xff0c;就是所谓的科班出身。说不会&#xff0c;是因为实践能力极差。 不会的问题&#xff0c;集中体现在毕设的时候&#xff0c;系…

CTFSHOW菜狗杯 web

文章目录web签到web2 c0me_t0_s1gn我的眼里只有$抽老婆一言既出驷马难追TapTapTapWebshell化零为整无一幸免传说之下&#xff08;雾&#xff09;算力超群算力升级2遍地飘零茶歇区小舔田&#xff1f;LSB探姬Is_Not_Obfuscate龙珠NFTweb签到 eval($_REQUEST[$_GET[$_POST[$_COOK…

Ubuntu22.04虚拟机配置双网

文章目录Ubuntu22.04虚拟机配置双网一、 虚拟机网络1、 简介1.1 概述1.2 四种网络2、 配置双网2.1 NAT2.2 主机模式3、 添加到虚拟机二、 ubuntu设置Ubuntu22.04虚拟机配置双网 一、 虚拟机网络 1、 简介 1.1 概述 近期在使用VirtualBox的时候遇到这样的场景&#xff0c;我…

Docker(五)—— 镜像原理、容器快照commit

一、如何得到镜像 1&#xff09;从远程仓库下载 2&#xff09;朋友/同事拷贝给你 3&#xff09;自己制作DockerFile 二、联合文件系统 Docker的镜像是由一层层的文件系统组成&#xff0c;这种层级的文件系统叫做联合文件系统UnionFS。 三、Docker镜像加载原理 1. bootfs:…

第十四届蓝桥杯校内模拟赛第一期——Python

第十四届蓝桥杯校内模拟赛第一期——Python 文章目录第十四届蓝桥杯校内模拟赛第一期——Python1.二进制位数问题描述参考答案扩展2. 晨跑问题描述参考答案扩展3. 调和级数问题描述参考答案4. 山谷问题描述参考答案5. 最小矩阵问题描述参考答案6. 核酸日期问题描述输入格式输出…

redux与react-redux的学习笔记之react-redux

redux与react-redux前言一、redux和react-redux是啥&#xff1f;二、redux使用步骤1.引入库2.原理图原理图释义actions1&#xff09;含义2&#xff09;demostore.js1&#xff09;含义2&#xff09;demoreducer.js1&#xff09;含义2&#xff09;demoCount.jsx1&#xff09;含义…