提示:若转载请备注来源,谢谢
前言
接上篇文章《C++基础 第一篇》:
知识点总结
知识点1:重载运算符
记住以下几点:
- 重载运算符是为了使对象操作更美观的技术,是C++中多态的一种表现形式。**重载运算法都是和类结合使用的,只有能使涉及类的代码更易读、易写时,使用运算符重载再有意义。
- 重载运算符的表现形式为operator@(…),@为运算符,括号内为运算符的参数。其中,运算符的参数,取决于两个因素:
2.1 运算符本身是一元(一个参数),还是二元(两个参数,如+、-等)
2.2 运算符被定义为类内的成员函数,还是全局函数。(一般很少使用全局函数定义)
3.可以重载的运算符如下:
1、运算符 + (-同理)
若不定义重载运算符,两个对象相加,会报错,如下:
NUMBER.h
class NUMBER
{
public:
NUMBER();
NUMBER(int num);
~NUMBER();
NUMBER(const NUMBER &number);
void show();
private:
int number;
};
NUMBER.cpp
#include "NUMBER.h"
#include <iostream>
using namespace std;
/* -构造函数 */
NUMBER::NUMBER()
{
cout << "NUMBER 构造函数" << endl;
}
NUMBER::NUMBER(int num)
{
cout << "NUMBER 构造函数重载" << endl;
this->number = num;
}
/* -析构函数 */
NUMBER::~NUMBER()
{
cout << "NUMBER 析构函数" << endl;
}
/* -拷贝构造函数 */
NUMBER::NUMBER(const NUMBER &c_num)
{
cout << "NUMBER 拷贝构造函数" << endl;
this->number = c_num.number;
}
void NUMBER::show()
{
cout << "number的值为 " << this->number << endl;
}
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "NUMBER.h"
using namespace std;
void test(void)
{
NUMBER num_a(5);
NUMBER num_b(6);
NUMBER num_c;
num_c = num_a + num_b; // 这里报错
cout << "num_a所占空间大小: " << sizeof(NUMBER) << endl;
num_a.show();
num_b.show();
num_c.show();
}
int main(void)
{
test();
system("pause");
return EXIT_SUCCESS;
}
正确写法,在NUMBER.h中加入+的重载运算符的声明,并在NUMBER.cpp定义
// 声明
NUMBER operator+(const NUMBER& c_num);
// 定义
NUMBER NUMBER::operator+(const NUMBER & c_num)
{
cout << "+运算符重载" << endl;
NUMBER var;
var.number = c_num.number + this->number;
return var; // 调用拷贝构造函数
}
2、运算符 =
=号运算符在编程中是最基本的运算符,常常使人困扰,因为可以进行赋值操作,也可以引起拷贝构造函数的调用。
当我们在类中写深拷贝或者赋值运算符重载时,是根据实际需要写的,若类中成员变量没有指针,我们可以不用写,默认的就可以实现功能
记住:
- 如果一个对象还没有创建,则必须要先经过初始化,也是调用拷贝构造(如下);
- 若一个函数构建了,若进行了=运算符重载操作,才会执行operator=函数;
- 若返回引用(this),则可以进行链式操作, 对象的链式编程,返回的一定是引用this;
- 当我们定义一个类时,系统默认帮我们定义了四个函数:
1,默认构造函数
2,默认拷贝构造函数
3,析构函数
4,一个等号=运算符重载函数
NUMBER& NUMBER::operator=(const NUMBER& c_num)
{
cout << "=运算符重载" << endl;
this->number = c_num.number;
return *this;
}
main.cpp,区分
void test(void)
{
NUMBER num_a(5);
NUMBER num_b(6);
NUMBER num_c = num_a; // 拷贝构造
cout << "****** " << endl;
num_c = num_a; // =运算符运算重载
cout << &num_a << endl; // 两地址不同
cout << &num_c << endl;
num_a.show();
num_b.show();
num_c.show();
}
实例2:知道了怎么进行=号运算符重载,那需要知道为什么进行运算符重载,例如下面例子,带有指针的=重载,若我们不人为的进行=号重载,而是使用默认的=,就会造成堆区的指针指向同一块区域,不信的话,可以屏蔽到PERSON& PERSON::operator=(const PERSON &person)试试,析构时一定会出错。
PERSON.h文件
class PERSON
{
public:
PERSON(const char *name, int age);
PERSON& operator=(const PERSON &person);
~PERSON();
void show(void);
private:
char* pname;
int age;
};
PERSON.c文件
void PERSON::show()
{
cout << this->pname << endl;
cout << this->age << endl;
}
PERSON::PERSON(const char *name, int age)
{
this->pname = new char[strlen(name) + 1];
strcpy(this->pname, name);
this->age = age;
}
PERSON::~PERSON()
{
cout << "PERSON 析构函数" << endl;
if (this->pname != NULL)
{
delete[] this->pname;
}
}
PERSON& PERSON::operator=(const PERSON &person)
{
cout << "=运算符重载" << endl;
// 注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存
// 这个时候如果直接赋值,若之前的值没有完全覆盖,会导致等号两边内存块的大小不相等
//所以,需要先释放后重新分配
if (this->pname != NULL)
{
delete[] this->pname;
}
this->pname = new char[strlen(person.pname) + 1];
strcpy(this->pname, person.pname);
this->age = person.age;
return *this;
}
test.c如下
void test_01(void)
{
PERSON person1("cczhai",20);
PERSON person2("niuniuxue", 25);
person1 = person2;
person1.show();
person2.show();
}
3、运算符 *和->
问题引出:当创建对象的指针后,该指针需要指向new开辟出的空间,往忘记释放会造成空间的浪费,所以,出现了智能指针(可以理解为类),我们需要在智能指针类内完成*和->运算符的重载。
问题引出:类还是使用上面的PERSON类
void test_02(void)
{
定义PERSON类指针,指针指向PERSON类型的地址空间
该地址空间使用new开辟,并给该地址空间内的参数赋值
可以理解为把某一块地址强制类型转换
PERSON *person1 = new PERSON("cczhai", 20); // 有参构造创建
PERSON *person2 = new PERSON(*person1); // 拷贝构造创建(理解)
cout << "****" << endl;
person1->show();
}
从运行结果,可以发现test_02函数执行完后,并没有调用析构函数,这样做有一定的风险,所以引入了智能指针,用于托管对象指针(new出的对象),使得该对象空间可以完成自动释放的功能。
智能指针
p_PERSON用于维护PERSON类
class p_PERSON
{
public:
p_PERSON(PERSON *person);
~p_PERSON();
// 返回指针
PERSON* operator->();
PERSON& operator*();
private:
PERSON *person; // 托管一个PERSON类型的指针
};
p_PERSON::p_PERSON(PERSON *person)
{
cout << "p_PERSON 有参构造函数" << endl;
this->person = person;
}
p_PERSON::~p_PERSON()
{
cout << "p_PERSON 析构函数" << endl;
if (this->person != NULL)
{
delete this->person;
this->person = NULL;
}
}
PERSON* p_PERSON::operator->()
{
return this->person;
}
PERSON& p_PERSON::operator*()
{
return *this->person;
}
测试:
void test_03(void)
{
p_PERSON person1(new PERSON("cczhai",20));
person1->show();
(*person1).show();
cout << "****" << endl;
}
结果:
4、运算符 ()
运算符()主要用在仿函数中,仿函数不是函数,是类的对象,看着像函数
这里先介绍一下()的用法,很简单。接上面的类。
class myprint
{
public:
myprint();
~myprint();
void operator()(string text);
};
myprint::myprint()
{
cout <<"构造" << endl;
}
myprint::~myprint()
{
cout << "析构" << endl;
}
void myprint::operator()(string text)
{
cout << text<< endl;
}
void test_04(void)
{
myprint MyPrint;
MyPrint("你好"); // 仿函数
// 匿名函数本行构造完后,接着析构
myprint()("我好"); // 匿名函数需要加(),写本质
cout << "****" << endl;
}
5、运算符 >>和 <<
C++标准库已经在ostream 和istream 中对>>和<<进行了重载(cout是 ostream 类的对象,cin是 istream 类的对象),所以我们没有办法在自定义的类中在对直接对其重载,但是我们可以以友元函数,使用全局变量对其重载。
class Complex
{
public:
friend ostream& operator<< (ostream &out, Complex &num);
friend istream& operator>> (istream &in, Complex &num);
Complex(double r = 0, double i = 0) :real(r), imag(i){};
private:
double real;
double imag;
};
// ostream 类将<<重载为成员函数,我们无法修改ostream内部,所以只能将运算符重载为全局函数
ostream& operator<< (ostream &out, Complex &num)
{
out << num.real << "+" << num.imag << "i"; // 所有的数据都会输入到out中,然后输出
return out;
}
void string2double(string str, double &num)
{
stringstream ss;
ss << str;
ss >> num;
}
istream& operator>> (istream &in, Complex &num)
{
string str;
in >> str;
int pos = str.length();
//str = str.erase(pos, 1);
pos=str.find("+", 0);
string2double(str.substr(0, pos),num.real);
string2double(str.substr(pos), num.imag);
return in;
}在这里插入代码片
知识点2: 匿名构造函数
** 匿名构造函数,构造完之后立马析构: **
形式:类名(参数)
例如:PERSON("cczhai",20); // 有参构造完,立马析构
总结
关于运算符重载,在实际使用中用的不多,我们要做的是看代码时,能够看懂,当我们实际写代码时,能够照着别人的代码写出来。
``