还是用代码做笔记吧
#ifndef _CLASSDEMO_H_
#define _CLASSDEMO_H_
namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num);
ClassDemo &operator=(const ClassDemo &other);
ClassDemo(const ClassDemo &other); // 拷贝构造函数,它的格式是固定的,它的格式无法发生改变,所以它是无法重载的
// 那么我们不写,编译器会默认帮我们生成一个,如果写了,编译器就不再生成了
//// 如果我们写了这样一个函数,那么就永远无法通过编译的,会涉及到一个东西是 无限递归
//// 那么为什么会进行无限递归呢?原因很简单,因为形参在被调用的时候,要被实参化,实参化就是进行一个拷贝,那么在拷贝的时候
//// 就会调用一个构造函数(即拷贝构造函数,那么拷贝构造函数就是它本身),所以就变成了无限的递归
//ClassDemo(const ClassDemo other)
//{
//
//}
~ClassDemo();
int GetNum() const;
private:
int _num;
};
}
#endif // !_CLASSDEMO_H_
#include "ClassDemo.h"
#include <iostream>
namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo(" << _num << ")" << std::endl;
}
// 初始化列表
ClassDemo::ClassDemo(int num) : _num(num)
{
std::cout << "ClassDemo(" << num << ")" << std::endl;
}
ClassDemo& ClassDemo::operator=(const ClassDemo& other)
{
std::cout << "operator=(const ClassDemo& other)" << _num << std::endl;
_num = other._num;
return *this;
}
ClassDemo::ClassDemo(const ClassDemo& other)
{
std::cout << "ClassDemo(const ClassDemo& other)" << _num << std::endl;
_num = other._num;
}
ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo(" << _num << ")" << std::endl;
}
int ClassDemo::GetNum() const
{
return _num;
}
}
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "ClassDemo.h"
// 模拟所有的int操作
class Integer
{
public:
// 1. operator函数可以重载
// 2. 符号 运算符 = > < == != + - & | ! new() sizeof()
// 无法重载的运算符(总共有5个) ::(作用域运算符) ? :(三目运算符) .(直接成员访问运算符) ->(间接成员访问运算符) *.(应该叫类成员指针运算符)
// 其实上面五个运算符也没有重载的必要性
// &(取地址运算符),它也是默认生成的一个函数,它return的是this,这个我们一般也不用去管
//
Integer(int num = 0) :_num(num)
{
std::cout << "Integer(int num = " << _num << ")" << std::endl;
}
// 拷贝构造函数
Integer(const Integer &other) : _num(other._num)
{
std::cout << "Integer(const Integer &other = " << _num << ")" << std::endl;
}
// 默认的operator=函数返回的是当前对象的引用
// 那么我们写成 void 类型有问题吗?我们获取一下 _num 看看,我们赋值一次没有问题
// 但是如果我们想多次进行赋值,那么返回void就会出问题了
/*void operator=(const int num)
{
_num = num;
std::cout << "operator=(" << num << ")" << std::endl;
}*/
Integer &operator=(const int num)
{
_num = num;
std::cout << "operator=( int " << num << ")" << std::endl;
return *this;
}
// 默认的赋值函数
Integer &operator=(const Integer &other)
{
std::cout << "operator=( const Integer & )" << _num << std::endl;
_num = other._num;
return *this;
}
// 这个和上面的void一样,也是不能连续赋值
//void operator+(int num)
//{
// _num += num;
//}
Integer operator+(int num)
{
Integer temp;
temp = _num + num;
return temp;
}
// friend修饰后的函数,这个函数就不属于我们的类了,所以也就没有this指针了,
// 即使我们写到类的里面,也不是类的成员函数
friend Integer operator+(int num, const Integer &other)
{
Integer temp;
temp._num = num + other._num;
return temp;
}
// ++是一个一元操作符
// 前++调用不带参数的operator++
Integer &operator++()
{
++_num;
return *this;
}
// 后++调用带参数的operator++
Integer operator++(int)
{
Integer temp = *this;
++_num;
return temp;
}
// 关系运算符
bool operator==(const Integer &other)
{
return _num == other._num;
}
// << >>
// 流运算符,输出输入
friend std::ostream &operator<<(std::ostream &os, const Integer &other)
{
os << other._num;
return os;
}
friend std::istream &operator>>(std::istream &os, Integer &other)
{
os >> other._num;
return os;
}
int GetNum()
{
return _num;
}
~Integer()
{
std::cout << "~Integer(" << _num << ")" << std::endl;
}
private:
int _num;
};
Integer f1()
{
return Integer(10);
}
Integer &f2()
{
return Integer(20);
}
int main()
{
using namespace PoEdu;
// demo1
// 当前面没有参数进行接收的时候,会打印出这样的结果
// Integer(int num = 10)
// ~Integer(10)
// Integer(int num = 20)
// ~Integer(20)
//f1();
//f2();
// demo2
// 那么,当前面有对象进行接收的时候,又会是怎么样呢?
// Integer(int num = 10)
// Integer(int num = 20)
// ~Integer(20)
// Integer(const Integer &other = 16039936)
//Integer i1 = f1(); // 构造函数
//Integer i2 = f2(); // 构造函数
// demo3
Integer i1; // 默认构造函数
Integer i2; // 默认构造函数
i1 = f1();
i2 = f2();
// 上面的代码会调用下面的
// Integer(int num = 0)
// Integer(int num = 0)
// Integer(int num = 10)
// operator=(const Integer &)0
// ~Integer(10)
// Integer(int num = 20)
// ~Integer(20)
// operator=(const Integer &)0
// 从上面的三个例子可以看出,返回一个临时对象和返回一个临时对象的引用是不一样的,区别在于
// 1. 返回对象,会在函数结束后,并将值赋值给其它对象后,才会析构
// 2. 而返回引用,则在当前方法调用完之后,直接析构
// 这就是两者最大的区别
Integer demo = 10, other = 20;
//other = demo = 100;
// 如果我们不写赋值函数,那么我们就需要下面三个步骤才能完成上面的操作
// 1. 构造 temp 对象 Integer(100)
// 2. 调用 operator=(const Integer &other) 赋值函数
// 3. 析构 temp 对象
// 所以为了提高效率,我们可以对operator=的方法进行重载
//Integer *p = &demo;
// 此时没有问题,说明生成了 &运算符
//Integer other = *p;
// 说明生成了解指针运算符
// 所以一个空类当中,默认的给我们生成了以下六个函数
// 构造
// 析构
// 赋值
// 拷贝构造函数
// operator&
// operator*
other = demo = 100;
// 我们将这一句话进行分解,分解过程如下所示,如果返回值是void,
// 那么other.operator=(demo.operator=(100))这句话就有问题了,所以,返回值必须是对象的引用
//demo = 100;
//demo.operator=(100);
//other = demo;
//other.operator=(demo);
//other.operator=(demo.operator=(100));
other = demo + 1;
other = 10 + demo;
std::cout << demo.GetNum() << std::endl;
std::cout << other.GetNum() << std::endl;
demo = 100 + other + 1000;
// 它会是怎么样的一个调用顺序呢?
// friend operator+ 赋值
// 为什么会是这样的顺序呢?暂时还不清楚,先记住吧
demo++;
++demo;
std::cout << demo << std::endl;
return 0;
}