【C++】拷贝构造函数和运算符重载

【C++】拷贝构造函数和运算符重载_第1张图片

文章目录

      • 1.拷贝构造的补充
        • 1.1自定义类型的拷贝构造
        • 1.2总结
      • 2.运算符重载
        • 2.1特征
        • 2.1**C++最常用的处理方法**
        • 2.3日期类的赋值运算符重载
        • 2.4操作符重载的规则

1.拷贝构造的补充

编译器默认生成的拷贝构造:默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝称为浅拷贝。

  • 不写拷贝构造,编译器会自动生成默认拷贝构造,拷贝方式为浅拷贝

  • 默认构造函数:对于内置类型成员值拷贝,浅拷贝:按照字节序传值。

  • 默认构造函数:对于自定义类型,去调用自定义类型的拷贝构造

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;

		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
		_top = 0;
		_capacity = capacity;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

比如上述程序会出现内存问题。Stack内部没有定义拷贝函数,所以编译器会自动生成拷贝构造函数,拷贝方式为浅拷贝。由于Stack成员变量为指针,按照内存序拷贝拷贝的是指针,因此st1和st2指向的是同一个位置。同一块内存空间析构两次所以会出现内存问题。

【C++】拷贝构造函数和运算符重载_第2张图片

【C++】拷贝构造函数和运算符重载_第3张图片

浅拷贝:

  • 1.指向同一块空间,修改数据会相互影响
  • 2.这块空间析构时会释放两次,程序会崩溃。
  • 解决方案:实现深拷贝

程序2

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_top = 0;
		_capacity = capacity;
        //memset是按照一个字节一个字节进行赋值
        //所以初始化最好每个字节初始化为0
		memset(_a, 0, sizeof(int) * 10);
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		_capacity = _top = 0;
	}
private:
	int _a[10];
	int _top;
	int _capacity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

【C++】拷贝构造函数和运算符重载_第4张图片

如果存放的是数组,可以正常的拷贝。因为st1存储的数组和st2存储的数组指向的不是同一块空间。

【C++】拷贝构造函数和运算符重载_第5张图片

1.1自定义类型的拷贝构造

自定义类型:去调用自定义类型的拷贝构造

程序一

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;
		_a = (int*)malloc(sizeof(int)*capacity);
		assert(_a);
		_top = 0;
		_capacity = capacity;
	}
    Stack(Stack& st)
    {
        cout<<"调用Stack的拷贝构造"<<endl;
    }
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
class MyQueue{
    private:
    	int _size=0;
    	Stack _st1;
    	Stack _st2;
}
int main()
{
    MyQueue mq1;
    MyQueue mq2(mq1);
    return 0;
}

MyQueue既有自定义类型又有内置类型。对于内置类型,编译器会继续浅拷贝,对于自定义类型调用自定义类型的拷贝构造

1.2总结

1.用户不写拷贝构造编译器会默认生成一个拷贝构造

2.自定义类型成员,去调用这个成员的拷贝构造

3.一般类,自己生成的拷贝构造就够用了。只有像Stack类这种需要自己直接管理资源需要实现深拷贝。

4.对于在堆区上开辟了空间的类,就需要使用深拷贝。

【C++】拷贝构造函数和运算符重载_第6张图片

2.运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

2.1特征
  • 1.不能通过连接其他符号来创建新的操作符:比如operator@

  • 2.重载操作符必须有一个类类型或者枚举类型的操作数

  • 3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义

  • 4.作为类成员的重载函数时,其形参看起来比操作数数目少1

    成员函数的操作符有一个默认的形参this,限定为第一个形参

    1. *. 、:: 、sizeof 、?: 、. **注意以上5个运算符不能重载。

    .* 域作用符,sizeof ,三目操作符 .操作符

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
//如果重载定义在类外,为了能够访问成员变量,就需要把成员变量修改为public类型;
//但是这种方法不够好。
//private:
	int _year;
	int _month;
	int _day;
};
//自定义类型,不能直接用各种运算符
//为了自定义类型可以使用各种运算符,制定了运算符重载的规operator 
//返回值 operator运算符 参数列表
bool operator==(Date d1,Date d2)
{
    return d1._year==d2._year
        &&d1._month==d2._month
        &&d1._day==d2._day;
}
//操作符重载的使用方式:
bool test(Date d1,Date d2)
{
    return d1==d2
}

而Python和Java习惯使用内置返回值函数。还有一种方法是使用有元,但是有元会破坏封装。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //返回私有变量。
    int Getyear()
    {
        return _year;
    }
    //........
private:
	int _year;
	int _month;
	int _day;
};
bool operator==(Date d1,Date d2)
{
    return d1.Getyear()==d2.Getyear()
        &&d1.Getmonth()==d2.Getmonth()
        &&d1.Getday()==d2.Getday();
}
bool test(Date d1,Date d2)
{
    return d1==d2
}
2.1C++最常用的处理方法

C++最常用的是把运算符重载定义为成员函数

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    bool operator==(const Date&d)
    {
        return _year==d._year
        &&_month==d._month
        &&_day==d._day;
    }
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    Date d1(2022,5,17);
    Date d2(2022,5,17);
    if(d1==d2)
    {
        cout<<"=="<<endl;
    }
    return 0;
}
//输出:==
2.3日期类的赋值运算符重载
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //赋值运算符的返回值,返回值是赋值运算符的左值,因为赋值运算符要满足连续赋值 k=i=j j先赋值给i,i再赋值给k;
    //如果出了作用域对象还存在,可以使用引用返回;
    //返回值不可以使用const:比如(k=i)=j;如果返回值是const类型
    //那么(k=i)的返回值是const k,j就不能够对k进行下一步赋值。
    Date& operator=(const Date&d)
    {
        if(this!=&d)
       //如果使用if(*this!=d),就需要重载!=。得不偿失
        {
            _year=d._year;
            _month=d._month;
            _day=d._day;
            //对this指针进行解引用z
        }
        return *this;
    }
private:
	int _year;
	int _month;
	int _day;
};
2.4操作符重载的规则

对于该类有意义的运算符才进行运算符重载;比如日期类相加就无意义,所以就不需要操作符重载。

【C++】拷贝构造函数和运算符重载_第7张图片

你可能感兴趣的:(c++,c++,visual,studio,开发语言)