C++基础知识点 第二篇

提示:若转载请备注来源,谢谢


前言

接上篇文章《C++基础 第一篇》:

知识点总结

知识点1:重载运算符

记住以下几点:

  1. 重载运算符是为了使对象操作更美观的技术,是C++中多态的一种表现形式。**重载运算法都是和类结合使用的,只有能使涉及类的代码更易读、易写时,使用运算符重载再有意义。
  2. 重载运算符的表现形式为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、运算符 =

=号运算符在编程中是最基本的运算符,常常使人困扰,因为可以进行赋值操作,也可以引起拷贝构造函数的调用。
当我们在类中写深拷贝或者赋值运算符重载时,是根据实际需要写的,若类中成员变量没有指针,我们可以不用写,默认的就可以实现功能
记住:

  1. 如果一个对象还没有创建,则必须要先经过初始化,也是调用拷贝构造(如下);
  2. 若一个函数构建了,若进行了=运算符重载操作,才会执行operator=函数;
  3. 若返回引用(this),则可以进行链式操作, 对象的链式编程,返回的一定是引用this;
  4. 当我们定义一个类时,系统默认帮我们定义了四个函数:
    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);		// 有参构造完,立马析构

总结

关于运算符重载,在实际使用中用的不多,我们要做的是看代码时,能够看懂,当我们实际写代码时,能够照着别人的代码写出来。
``

猜你喜欢

转载自blog.csdn.net/zcc_123/article/details/112392165