c++自定义智能指针和string类

时间:2023-01-06 16:41:23

main.cpp

#include "MyString.h"

//智能指针的意义是你new一个空间,是指针指向这个空间,不用在调用delete去回收指针指向的空间,系统会自己帮我们回收
int main()
{
    //一般指针开辟空间
    int *p = new int;
    //智能指针开辟空间
    //anto_ptr<int>等价于int * ,其本质为指针的类型,<>括号里存储的为指针开辟空间存储的数据类型,ptr为指针名,(new int)表明其在堆中开辟空间,返回给指针ptr
    //也可以这样理解,auto_ptr<>即为一种模板类,(new int)的地址返回值为构造函数的参数,ptr即为一个对象,不过这个对象为指针
    auto_ptr<int> ptr1;
    auto_ptr<int> ptr2(new int);
    *ptr2 = 10;

    //下面一段代码ptr3为Test *类型的指针,auto_ptr<Test>等价于Test * ,(new Test(10))是表示调用类Test的有参构造函数同时在堆中开辟空间,并把空间地址返回给指针ptr
    auto_ptr<Test> ptr3(new Test(10));

    Test *t = new Test(10);
    (*t).function();//*是直接取指针指向的对象,但.的运算优先级高于* ,因此需要加小括号
    delete t;
    
    MyAutoPtr ptr4(new Test(100));
    //注意,这里的ptr4是MyAutoPtr类型的对象,并不是new出来的Test类型对象的指针,ptr4.ptr才为new出来的Test类型对象的指针
    //因此对象ptr4如果直接想调用它内部指针指向对象的函数,则需要重写->操作符
    //这儿其实执行完ptr4->返回指针ptr,此时应该就没有了->操作符,但这儿其实还留有->操作符给返回的指针使用,难道是因为->只与前面参数有关,所有与前面有关的操作符在执行完后这个操作符仍要继续使用一次???
    ptr4->function();//先写出代码式子,在根据式子重写操作符,等价于ptr4.operator->();
    (*ptr4).function();//这时应返回指针指向对象的本身

    cout << endl << endl << endl;

    Test1 t1(1);
    Test2 t2(2, t1);

    string s1 = "123";
    s1 = "1212";//重载了=操作符
    string s2 = "456";
    cout << "s1+s2=" << s1 + s2 << endl;//重载了+操作符
    //cin >> s1[1];//重载了>>操作符
    s1[1] = 'q';//重载了[]操作符
    if (s1 == s2)//重载了==操作符
    {

    }
    if (s1 != s2)//重载了!=操作符
    {

    }
    //int a, b;
    //cin >> a >> b;//cin也可以连续输入

    /*
    char *c = NULL;
    char *d = NULL;
    strcpy(c, d);//不可以对指向为空的指针进行拷贝
    */

    char *c = new char[3];
    const char *d = "111";
    char e[3];
    char f[3] = { 0 };
    //strlen()函数是遇'\0'结束的,在new来的空间时,因为最后一位不是'\0'因此strlen()函数仍会继续向下相加,直到遇到第一个'\0'为止才停止计算,因此用strlen计算数组是不准确的,最好只用来计算字符串的长度
    cout << "strlen(c)=" << strlen(c) << endl;//结果是未知的
    cout << "strlen(d)=" << strlen(d) << endl;//结果是3
    cout << "strlen(e)=" << strlen(e) << endl;//结果是未知的
    cout << "sizeof(c)=" << sizeof(c) << endl;//结果是0,因为数组内都是'\0',strlen()函数遇'\0'结束
    //strcpy()与strlen()不同,strcpy()函数在不是字符串的时候就会停止,而strlen()只会在遇到'\0'才停止

    MyString m1;
    MyString m2("xuhaoxuan");
    MyString m3;
    MyString m4("xuqiang ");
    m3 = m1 + m2;
    cout << "111=" << m3;
    cout << "222=" << m4 + m2;
    return 0;

}


MyString.cpp

#include "MyString.h"

MyString::MyString()
{
    this->len = 0;
    this->str = NULL;
}

MyString::MyString(int len)
{
    if (len = 0)
    {
        this->len = 0;
        this->str = NULL;
    }
    else
    {
        this->len = len;
        this->str = new char[len];
    }
}

MyString::MyString(const char *str)
{
    if (str == NULL)
    {
        this->len = 0;
        this->str = NULL;
    }
    else
    {
        this->len = strlen(str) + 1;
        this->str = new char[len];
        strcpy(this->str, str);
    }
}

MyString::MyString(const MyString &s)
{
    if (s.str == NULL)
    {
        this->len = 0;
        this->str = NULL;
    }
    else
    {
        this->len = s.len;
        this->str = new char[len];
        strcpy(this->str, s.str);
        cout << "copy function=" << this->str << endl;
    }
}

MyString &MyString::operator=(const MyString &s)//类型不用加作用域,只有类里的成员方法才需要加类名作用域,记住operator+等这些操作符重载函数也是成员方法,一定需要带上类名作用域
{
    if (s.str == NULL)
    {
        this->len = 0;
        this->str = NULL;
    }
    else
    {
        this->len = s.len;
        if (this == &s)
        {
            return *this;
        }
        if (this->str != NULL)
        {
            delete[] this->str;
        }
        this->str = new char[len];
        strcpy(this->str, s.str);
    }
    return *this;
}

char &MyString::operator[](int index)
{
    return this->str[index];
}

bool MyString::operator==(const MyString &s)
{
    int key = strcmp(this->str, s.str);//相等返回0,this->str大于s.str返回正数,this->str小于s.str返回负数
    if (key == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool MyString::operator!=(const MyString &s)
{
    int key = strcmp(this->str, s.str);
    if (key == 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}

char *MyString::getStr()const
{
    return str;
}

MyString MyString::operator+(const MyString &s)
{
    MyString temp;
    if (this->str == NULL && s.str == NULL)
    {
        return temp;
    }
    if (this->str == NULL && s.str != NULL)
    {
        temp.len = s.len;
        cout << "temp.len=" << temp.len << endl;
        temp.str = new char[temp.len];
        strcpy(temp.str, s.str);
        cout << "s.str=" << s.str << endl;
        cout << "temp.str=" << temp.str << endl;
        return temp;
    }
    if (this->str != NULL && s.str == NULL)
    {
        temp.len = this->len;
        temp.str = new char[temp.len];
        strcpy(temp.str, this->str);
        return temp;
    }
    if (this->str != NULL && s.str != NULL)
    {
        temp.len = this->len + s.len;
        temp.str = new char[temp.len];//new开辟出来的空间里面的值是随机的,并不是0
        memset(temp.str, 0, temp.len);
        strcpy(temp.str, this->str);
        char *point = temp.str;
        while (1)
        {
            if ( *point == '\0')
            {
                break;
            }
            point++;
        }
        strcpy(point, s.str);
        return temp;
    }
    return temp;
}

MyString::~MyString()
{
    if (this->str != NULL)
    {
        delete[] str;//不能释放NULL
    }
}

ostream &operator<<(ostream &out, const MyString &s)
{
    if (s.getStr() == NULL)//因为在这个方法作用域内s都是不可改变的,所以在这期间所有用到s对象的函数都需要用const修饰
    {
        return out;
    }
    else
    {
        out << s.getStr() << endl;
        return out;
    }

}

istream &operator>>(istream &in, const MyString &s)
{
    in >> s.str;
    return in;
}


//下面三段代码的写法是错误的,在返回引用的时候,变量a就可以改变,但这时a在test()函数中是不可改变的,这两个相冲突,所以在函数中被const修的变量是绝对不可以返回引用的
/*
int &test(const int a)
{
    return a;
}
*/


MyString.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <memory>//为系统自带的智能指针头文件,是用智能指针需要调用这个头文件
#include <string>//为系统自带的字符串类,string类里重写了很多操作符

using namespace std;
class MyString
{
public:
    friend istream &operator>>(istream &in, const MyString &s);//编译器会自己帮我们去包含这个头文件的.cpp文件找到这些函数的定义
    MyString();
    MyString(int len);//创建一个长度为len的字符串
    MyString(const char *str);//这是给定字符串的构造函数,但并不是拷贝构造
    MyString(const MyString &another);
    char *getStr()const;
    MyString &operator=(const MyString &s);
    char &operator[](int index);
    bool operator==(const MyString &s);
    bool operator!=(const MyString &s);
    MyString operator+(const MyString &s);
    ~MyString();


private:
    int len;
    char *str;
};

//new开辟出来的内存空间如果没有被delete,则将会在程序运行结束时操作系统自己回收,并不是在重启电脑时释放,网上很多讲解都是错误的!!!
class Test
{
public:
    Test(int a)
    {
        this->a = a;
    }
    void function()
    {
        cout << "-----------------------------------" << endl;
    }
    //一个对象在栈空间时,当进程结束时会自动调用析构函数回收这个对象
    //一个对象在堆空间时,你不使用delete去显式的释放这个对象,那么当进程结束时,不会调用析构函数,因此凡是new出来的对象,都需要手动的delete
    //在堆区间开辟出来的对象只有析构函数是不主动触发的,只有调用delete删除这个对象空间时,才会触发析构函数
    //注意这儿的delete与析构函数内部的delete完全不同,析构函数内部的delete是为了释放有类内部指针开辟的堆空间,而外部的delete是为了释放new出来的对象,但使用智能指针开辟对象,则就不需要手动释放了
    ~Test()
    {
        cout << "~Test()..." << endl;
    }
private:
    int a;
};

class MyAutoPtr
{
public:
    MyAutoPtr(Test *ptr)
    {
        this->ptr = ptr;
    }
    Test *operator->()
    {
        return this->ptr;
    }
    Test operator*()
    {
        return *ptr;
    }
    ~MyAutoPtr()
    {
        if (this->ptr != NULL)//因为ptr为void *类型,即万能指针,因此delete ptr时不会去调用类型Test的析构函数,它只是回去调void类型的析构函数,显然没有,所以不会触发Test类型的析构函数
        {
            delete ptr;
        }
    }
private:
    Test * ptr;
};

class Student
{
public:
    Student(int id, char *name)//这儿应有一个隐式的:name(),调用的可能是默认构造函数???
    {
        this->id = id;
        this->name = name;//一般的赋值应用strcpy函数,但我们调用了string类,并建立其对象,这个对象里其实有char *类型的指针,会开辟一个空间存储字符串,也可能是“=”“->”操作符重载,和智能指针一模一样???
    }
private:
    int id;
    string name;//string这个类即为char *的封装类,可以直接指向字符串,里面还重写了很多操作符,使字符串操作更简单便捷
};

class Test1
{
public:
    Test1(int a)
    {
        cout << "Test1()..." << endl;
        this->a = a;
    }
    Test1(const Test1 &t)//const看右边,只看一位,是谁就修饰谁,&看左边,可以看多为,是谁就修饰谁
    {
        cout << "Test1 copy function..." << endl;
        this->a = t.a;
    }
    int getA()
    {
        return this->a;
    }

    Test1 &operator=(const Test1 &t)
    {
        cout << "Test1 equal==============================" << endl;
        this->a = a;
        return *this;
    }
private:
    int a;
};

class Test2
{
public:
    Test2(int a, Test1 t100) :t(1000)//构造函数形参里的Test t100为调用拷贝构造函数构建的,构造函数初始化列表是调用构造函数建立的,这儿创建的对象即为这个类里的this->t
    {
        cout << "Test2->Test1 t.a=" << t.getA() << endl;
        this->a = a;
        this->t = t;//这儿调用的是=号操作符重载,并不是拷贝构造函数
        cout << "Test2 t.a=" << this->t.getA() << endl;
    }
private:
    Test1 t;
    int a;
};

istream &operator>>(istream &in, const MyString &s);
ostream &operator<<(ostream &out, const MyString &s);