2018-04-06 17:16:54 DSH2418C 阅读数 87
  • C++语言基础视频教程

    C++语言基础视频培训课程:本课与主讲者在大学开出的程序设计课程直接对接,准确把握知识点,注重教学视频与实践体系的结合,帮助初学者有效学习。本教程详细介绍C++语言中的封装、数据隐藏、继承、多态的实现等入门知识;主要包括类的声明、对象定义、构造函数和析构函数、运算符重载、继承和派生、多态性实现等。 课程需要有C语言程序设计的基础(可以利用本人开出的《C语言与程序设计》系列课学习)。学习者能够通过实践的方式,学会利用C++语言解决问题,具备进一步学习利用C++开发应用程序的基础。

    210670 人正在学习 去看看 贺利坚

操作符重载的概念

  • C++中的重载能够扩展操作符的功能

  • 操作符的重载以函数的方式进行

  • 本质:用特殊形式的函数扩展操作符的功能

  • 通过operator关键字定义特殊的函数来**重载操作符

  • 语法:

// Sign为系统中预定义的操作符
Type operator Sign(const Type& p1, const Type& p2)
{
    Type ret;

    return ret;
}
  • 可以将操作符重载函数定义为类的成员函数

    • 比全局操作符重载函数少一个参数(左操作数)

    • 编译器优先在成员函数中寻找操作符重载函数

  • 操作符重载的注意事项

    • 操作符重载不能改变原操作符的优先级

    • 操作符重载不能改变操作数的个数

    • 操作符重载不能改变操作符的原有语义


数组操作符([])重载

  • 数组操作符是C/C++中的内置操作符

  • 数组操作符的原生意义是数组访问指针运算

    • a[n] <==> (a + n) <==> (n + a) <==> n[a]
  • 数组访问操作符的重载能够使得对象模拟数组的行为

    • 只能通过类的成员函数重载

    • 重载函数能且仅能使用一个参数

    • 可以定义不同参数的多个重载函数

/*
    测试代码
*/

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int a[5];
public:

    int& operator [] (int i)
    {
        return a[i];
    }

    int& operator [] (const string& s)
    {
        if( s == "1st" )
        {
            return a[0];
        }
        else if( s == "2nd" )
        {
            return a[1];
        }
        else if( s == "3rd" )
        {
            return a[2];
        }
        else if( s == "4th" )
        {
            return a[3];
        }
        else if( s == "5th" )
        {
            return a[4];
        }

        return a[0];
    }

    int length()
    {
        return 5;
    }
};

int main()
{
    Test t;

    for(int i=0; i<t.length(); i++)
    {
        t[i] = i;
    }

    for(int i=0; i<t.length(); i++)
    {
        cout << t[i] << endl;
    }

    cout << endl;   

    cout << t["5th"] << endl;
    cout << t["4th"] << endl;
    cout << t["3rd"] << endl;
    cout << t["2nd"] << endl;
    cout << t["1st"] << endl;

    return 0;
}

运行结果

0
1
2
3
4

4
3
2
1
0

赋值操作符(=)重载

  • C++规定赋值操作符(=)只能重载为成员函数

  • 编译器为每个类默认重载了赋值操作符

  • 默认的赋值操作符仅完成浅拷贝

  • 当需要进行深拷贝时必须重载赋值操作符

  • 赋值操作符重载与拷贝构造函数有相同的存在意义

/*
    测试代码
*/

#include <iostream>

using namespace std;

class Test
{
private:

    int* m_pointer;

public:

    Test()
    {
        m_pointer = NULL;
    }
    Test(int i)
    {
        m_pointer = new int(i);
    }

    //自定义拷贝构造函数 
    Test(const Test& obj)
    {
        m_pointer = new int(*obj.m_pointer);
    }

    // 赋值操作符重载
    Test& operator = (const Test& obj)
    {
        if(this != &obj)
        {
            delete m_pointer;

            m_pointer = new int(*obj.m_pointer)
        }

        retutn *this;
    }

    void print()
    {
        cout << "m_pointer = " << m_pointer << endl;
    }

    ~Test()
    {
        delete m_pointer;
    }
};

int main()
{
    Test t1(1);

    Test t2;
    t2 = t1;

    t1.print();
    t2.print();

    return 0;
}

运行结果

m_pointer = 0x3810e8
m_pointer = 0x381108
2013-04-30 13:04:06 chijianxingfeng 阅读数 3083
  • C++语言基础视频教程

    C++语言基础视频培训课程:本课与主讲者在大学开出的程序设计课程直接对接,准确把握知识点,注重教学视频与实践体系的结合,帮助初学者有效学习。本教程详细介绍C++语言中的封装、数据隐藏、继承、多态的实现等入门知识;主要包括类的声明、对象定义、构造函数和析构函数、运算符重载、继承和派生、多态性实现等。 课程需要有C语言程序设计的基础(可以利用本人开出的《C语言与程序设计》系列课学习)。学习者能够通过实践的方式,学会利用C++语言解决问题,具备进一步学习利用C++开发应用程序的基础。

    210670 人正在学习 去看看 贺利坚

1.[ ]操作符重载

C++语言规定:“[ ]”只能作为类的成员函数进行重载。 “[ ]”是C++中的下标运算符,对于数组或指针来说,下表运算的语义是确定的,不能进行重载。因此,如果看到一个运算结果不是数组或指针的表达式后跟“[ ]”运算符,一定是对“[ ]”进行了重载。

一个例子:

#include <iostream>
using namespace std;

class A
{
	int num[3];
public:
	A();
	int& operator[](int);
};

A::A()
{
	num[0] = 1;
	num[1] = 2;
	num[2] = 3;
}

int& A::operator[](int sub)
{
	cout<<"you are in\n";
	if(sub < 0 || sub >2)
		throw sub;
	else
		return num[sub];
}

int main()
{
	A a;
	A *p = &a;
	try
	{
		for(int i = 0; i < 4; i++)
			//cout<<a[i]<<endl;		//这句和下面的作用一样。 这里也对[]进行了重载
			cout<<p[0][i]<<endl;	//第一个[]运算符是下标运算符的原意,p[0]相当于*p,代表指针所指向的对象
					//第二个[]运算符使用的是重载后的语义,表示从对象内部的成员数组中取数据
	}
	catch(int sub)
	{
		cout<<"subscript out of range:"<<sub<<endl;
	}
	return 0;
};

对 [ ]操作符重载,一般会将操作符函数的返回值定义为引用类型。因为这样找到数组元素后,既可以对它进行读操作,也可以进行写操作。

一般来说,数组下标的数据类型是整数,这是C/C++语言中下标的基本用法。但从语法的角度来说,对operator [] 操作符函数进行重载,并没有限制下标的数据类型,这样就可以在某些特殊的情况下使用特殊的数据类型作为下标,看下面的一个例子,你就没白了。。。。

以字符串作为数组下标:

#include <iostream>
#include <string>
using namespace std;

class Employee
{
	string name;
	string position;
public:
	Employee(string, string);
	string& operator[](string);
};

Employee::Employee(string n, string p)
{
	name = n;
	position = p;
}

string& Employee::operator[](string s)	//以字符串作为参数,而不是整型
{
	if(s == "name")
		return name;
	else if(s == "position")
		return position;
	throw s;
}

int main()
{
	Employee e1("忍者", "经理"), e2("核弹","职员");
	try
	{
		cout<<"e1's name is:"<<e1["name"]<<endl;	//重载了操作符
		cout<<"e1's position is:"<<e1["position"]<<endl;
	}
	catch(string s)
	{
		cout<<"error: not find "<<s<<endl;
	}
	return 0;
}


总结一下 重在operator[] 要注意的几点:

1.操作符函数operator[] 只接受一个参数,没有参数和多于一个参数都会造成编译错误,参数的类型可以是任何类型;

2.操作符函数operator[]返回值类型应该是引用类型,这是为了与传统的数组下标运算语义保持一致;

3.当a为一个数组时,a[i]和i[a]都是合法的,都表示数组a 的下标为i的元素。但是,如果a是一个重载了operator[]的类的对象,那么

i[a]的表示方法将引发编译错误。所以为了显示的表明a是一个数组,在使用下标运算符的时候可采用i[a]的表示方式。

2. *操作符重载

*操作符既可以友元函数的形式重载,也可以以成员函数的形式重载,但如果是后者,应这样定义*操作符函数:

T operator*()

{

return *p;

}   一般情况下重载 * 操作符都是以成员函数的形式进行的。

"*"是一个一元操作符,它作用于指针,表示去指针所指单元的内容。一般来说,对*操作符进行重载的类都含有一个指针。

一个例子:

#include <iostream>
using namespace std;

template<typename T>
class Data
{
	T *ptr;
public:
	Data(T *p)
	{
		ptr = p;
	}
	~Data()
	{
		delete ptr;
	}
	//以友元函数形式重载
	//template<typename T> friend T operator*(const Data<T>&);

	//以成员函数形式重载,,这样比友元函数简洁多了
	T operator*()
	{
		return *ptr;
	}
};

//友元函数
//template<typename T>
//T operator*(const Data<T>& d)
//{
//	return *(d.ptr);
//}

int main()
{
	Data<int> intData(new int(67));
	Data<double> doubleData(new double(22.2));
	cout<<*intData<<endl;	//重载了*操作符
	cout<<*doubleData<<endl;
	return 0;
}


3.赋值操作符重载

两种情况下需要对赋值操作符重载:

1.赋值号两边的表达式类型不一致(且无法进行转换)

2.需要进行“深拷贝”

一个浅拷贝的例子:

#include <iostream>
using namespace std;

class A
{
	int num;
public:
	A()
	{
		num = 0;
	}
	A(int i)
	{
		num = i; 
	}
	void show()
	{
		cout<<num<<endl;
	}
	A& operator= (int i)
	{
		cout<<"you are in operator\n";
		return *this;		//这里只是一个简单的浅拷贝
	}
};

int main()
{
	A a;
	a = 5;	//赋值操作,调用赋值操作符函数,,不过只是钱拷贝
	a.show();	//输出结果为 0,, 不是5
	return 0;
}


一个深拷贝的例子:

#include <iostream>
#include <string>
using namespace std;

class Student
{
	string name;
	int age;
public:
	Student()
	{
		name = "NULL";
	}
	Student(string s, int a)
	{
		name = s;
		age = a;
	}
	//拷贝构造函数
	Student(const Student& s)
	{
		*this = s;	//在这里调用操作符函数 operator=
	}
	void show()
	{
		cout<<"The student's name is:"<<this->name<<endl;
		cout<<"The student's age is:"<<this->age<<endl;
	}
	//实现的是深拷贝
	Student& operator=(const Student& s)
	{
		name = s.name.substr(0, s.name.length());
		age = s.age;
		return *this;
	}
};

int main()
{
	Student s1("张三", 18);
	Student s2("李四", 20);
	s1.show();
	Student s3 = s1;	//这里调用拷贝构造函数,在拷贝函数里面再调用 操作符函数 operator=
	s3.show();
	Student s4;
	s4 = s2;		//这里直接调用操作符函数 operator=
	s4.show();
	return 0;
}

注意要点:

对赋值操作符进行重载是,通常将操作符函数的返回值定义为赋值左操作数类型的引用,这是为了实现表达式的求值,也是为了实现“链式操作”。

4.输入输出操作符重载

输入操作符是>>, 输出操作符是<<,又叫做流对象的“插入操作符”和“提取操作符”。其实这两个操作符最初是在C语言中用于整数的移位操作,到了C++中才利用操作符重载的技术将它们应用于输入、输出操作。

对于基本数据类型的数据类型的输入输出操作都已经在C++标准库中定义好,没有必要重新定义,也不允许重新定义。而对于用户自定义的类来说,如果想利用输入输出操作符进行本类对象的输入输出操作,就需要对>>和<<操作进行重载。

对于输出操作符<<进行重载,只能采用友元函数的形式进行,而不能讲operator<<() 声明为ostream类的成员函数,这是因为,ostream是在C++中标准类库中定义的类,既然是标准库,就不允许用户随意修改。

对于输入操作符>>进行重载,和输出操作符一样,也是只能采用友元函数的形式进行。

输出操作符函数原型: ostream & operator<<(ostream&, const className&)   这里只列出最常用也是最安全的一个

输入操作符函数原型: istream & operator>>(istream&, className&)

一个例子:

#include <iostream>
using namespace std;

class Complex
{
	double real;
	double image;
public:
	Complex(double r = 0.0, double i = 0.0)
	{
		real = r;
		image = i;
	}
	//operator<< 和 operator>> 只能是作为友元函数
	//重载操作符<<
	friend ostream& operator<<(ostream&, const Complex&);
	//重载操作符>>
	friend istream& operator>>(istream&, Complex&);
};

ostream& operator<<(ostream& out, const Complex& c)
{
	out<<"in operator<<\n";
	out<<c.real<<" + "<<c.image<<"i"<<endl;
	return out;	//千万别忘了返回 out
}

istream& operator>>(istream& in, Complex& c)
{
	cout<<"in operator>>\n";
	bool flag = false;
	char ch;
	while(!flag)
	{
		cout<<"please input a complex:";
		in>>c.real;
		in>>ch;
		if(ch != '+')
			continue;
		in>>c.image;
		in>>ch;
		if(ch != 'i')
			continue;
		else
			flag = true;
	}
	return in;	//千万别忘了返回 in
}

int main()
{
	Complex c;
	cin>>c;
	cout<<c;
	return 0;
}


5.!操作符重载

“!”是一个一元操作符,它通常用于布尔量,表示逻辑取反。当在某个类中对 “!”操作符进行重载时,通常表明该类的对象出现了“异常”状况。 可以对”!“操作符进行重载,以表明用new 生成的对象是否构造成功。(new 操作在堆上申请空间)

对operator!进行重载可以有两种方式实现,既可以作为类的成员函数,也可以作为类 的友元函数。

具体情况看下面的例子:

#include <iostream>
using namespace std;

class A
{
	int m;
	char *str1;
	char *str2;
public:
	A(int n, int s1, int s2)
	{
		str1 = new char[s1];
		str2 = new char[s2];
		m = n;
	}
	~A()
	{
		delete str1;
		delete str2;
	}
	void show()
	{
		cout<<m<<endl;
	}

	//重载 ! 操作符, 作为A的成员函数
	//bool operator!()
	//{
	//	cout<<"int operator!()\n";
	//	if(str1&&str2)
	//		return false;
	//	return true;
	//}

	friend bool operator!(const A& a);
};

bool operator!(const A& a)
{
	cout<<"in frined operator!()\n";
	if(a.str1 && a.str2)
		return false;
	return true;
}

int main()
{
	A * p = new A(1, 2, 4);
	if(!p)	//这里只是普通的逻辑取反,并不调用重载函数operator!()
		cout<<"!p\n";
	if(!(*p))		//在这里调用重载函数operator!()
		cout<<"!(*p)\n";
	else 
		p->show();
	delete p;
	return 0;
}


 


 


2019-08-09 16:52:08 qq_32324999 阅读数 77
  • C++语言基础视频教程

    C++语言基础视频培训课程:本课与主讲者在大学开出的程序设计课程直接对接,准确把握知识点,注重教学视频与实践体系的结合,帮助初学者有效学习。本教程详细介绍C++语言中的封装、数据隐藏、继承、多态的实现等入门知识;主要包括类的声明、对象定义、构造函数和析构函数、运算符重载、继承和派生、多态性实现等。 课程需要有C语言程序设计的基础(可以利用本人开出的《C语言与程序设计》系列课学习)。学习者能够通过实践的方式,学会利用C++语言解决问题,具备进一步学习利用C++开发应用程序的基础。

    210670 人正在学习 去看看 贺利坚

操作符重载的规则与方法

  • C++中的重载能够扩展操作符的功能
  • 操作符的重载以函数的方式进行
  • 本质:用特殊形式的函数扩展操作符的功能
  • 通过operator关键字可以定义特殊的函数
  • operator的本质是通过函数重载操作符
#include<stdio.h>

class Complex
{
	int a;
	int b;
public:
	Complex(int a = 0, int b = 0 )
	{
		this->a = a;
		this->b = b;
	}
	int getA()
	{
		return a;	
	}
	int getB()
	{
		return b;	
	}
	friend Complex operator + (const Complex& c1, const Complex& c2); //为了在重载函数中少写调用成员函数的步骤,所以这里使用了友元
};
//重载加号操作符,使得可以计算负数的加法
Complex operator + (const Complex& c1, const Complex& c2)
{
	Complex ret;
	ret.a = c1.a + c2.a;
	ret.b = c1.b + c2.b;

	return ret;
}

int main()
{
	Complex c1(1,2);
	Complex c2(3,4);
	Complex c3 = c1 + c2;

	printf("c = (%d, %d) \n", c3.getA(), c3.getB());
	return 0;
}

将操作符重载定义为类的成员函数

  • 比全局操作符重载函数少一个参数(左操作数)
  • 不需要依赖友元就可以完成操作符重载
  • 编译器优先在成员函数中寻找操作符重载函数
#include<stdio.h>

class Complex
{
	int a;
	int b;
public:
	Complex(int a = 0, int b = 0 )
	{
		this->a = a;
		this->b = b;
	}
	int getA()
	{
		return a;	
	}
	int getB()
	{
		return b;	
	}
	
	Complex operator + (const Complex& p) //成员函数版本的操作符重载函数,只需要一个参数就够了,该参数为右操作数。
	{
		Complex ret;
		printf("Complex operator + (const Complex& p)\n");
		ret.a = this->a + p.a;
		ret.b = this->b + p.b;
	
		return ret;
	}

	friend Complex operator  +  (const Complex& c1, const Complex& c2);
};

Complex operator + (const Complex& c1, const Complex& c2)
{
	Complex ret;
	printf("Complex operator + (const Complex& c1, const Complex& c2)\n");

	ret.a = c1.a + c2.a;
	ret.b = c1.b + c2.b;

	return ret;
}
int main()
{
	Complex c1(1,2);
	Complex c2(3,4);
	Complex c3 = c1 + c2;  // c1.operator + (c2)
	printf("c = (%d, %d) \n", c3.getA(), c3.getB());
	return 0;
}

实验结果:

Complex operator + (const Complex& p)
c = (4, 6)

总结:

  • 操作符重载时C++的强大特性之一
  • 操作符重载的本质是通过函数扩展操作符的功能
  • operator关键字是实现操作符重载的关键
  • 操作符重载遵循相同的函数重载规则
  • 全局函数和成员函数都可以实现对操作符的重载(优先考虑成员函数的操作符重载)
2019-08-24 09:55:43 m0_37622246 阅读数 102
  • C++语言基础视频教程

    C++语言基础视频培训课程:本课与主讲者在大学开出的程序设计课程直接对接,准确把握知识点,注重教学视频与实践体系的结合,帮助初学者有效学习。本教程详细介绍C++语言中的封装、数据隐藏、继承、多态的实现等入门知识;主要包括类的声明、对象定义、构造函数和析构函数、运算符重载、继承和派生、多态性实现等。 课程需要有C语言程序设计的基础(可以利用本人开出的《C语言与程序设计》系列课学习)。学习者能够通过实践的方式,学会利用C++语言解决问题,具备进一步学习利用C++开发应用程序的基础。

    210670 人正在学习 去看看 贺利坚

操作符函数重载

操作符函数

1、在C++中针对类类型的对象的运算符,由于它们肯定不支持真正的运算操作,因此编译器会将它们翻译成函数,这种就叫操作符函数(运算符函数)。
2、编译器把运算符翻译成运算符函数,可以针对自定义的类类型设计它独有的运算功能。
3、其实各种运算符已经具备一些功能,再次实现它的就叫运算符重载。

双目运算符

a+b
成员函数
	a.operator+(b);
全局函数
	operator+(a,b);

单目运算符

!a
成员函数
	a.operator!(void);
全局函数
	operator!(a);

双目操作符函数重载

成员函数

const 类 operator#(const 类& that) const
{
	return 类(参数#参数);
}
注意:双目运算符的运算结果是个右值,返回值应该加const,然后为了const对象能够调用参数应写const,函数也应该具备const属性
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	const Point operator+(const Point& that) const
	{
		return Point(that.x+x,that.y+y);
	}
};

全局函数

类 operator#(const 类& a,const 类& b)
{

}
注意:全局函数不是成员函数,可能会访问到类的私有成员,解决这种问题可以把函数声明为类的友元函数(友元函数不是成员函数)。
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	friend const Point operator+(const Point& a,const Point& b);
};
const Point operator+(const Point& a,const Point& b)

	return Point(a.x+b.x,a.y+b.y);
}

友元函数

1、在类的外部某个想访问类的私有成员(public/protected/private)时,需要把所在的函数声明为友元,但友元只是朋友,没有实际的拥有权,因此它只有访问权(其根本原因是它没有this指针)。
2、友元函数的声明:把函数声明写一份到类中,在声明前加上friend关键字,使用友元既可以把操作符函数定义为全局的,也可以确保类的封装性。
注意:友元函数与成员函数不会构成重载关系,因为它们不在同一个作用域中。

赋值类型的双目运算符

1、获取单参构造与赋值运算符的调用方式。

String str = "sss"; // 会调用单参构造,而不会调用赋值运算符
str = "hehe"; // 会调用赋值运算符

2、左操作数不能具有const属性

1、成员函数不能是常函数
2、全局函数的第一个参数不能有const属性
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	/*
	const Point& operator+=(const Point& that)
	{
		x += that.x;
		y += that.y;
		return *this;
	} // 成员函数
	*/
	friend const Point& operator+=(Point& a,const Point& b);
};
const Point& operator+=(Point& a,const Point& b)
{
	a.x += b.y;
	a.y += b.y;
	return a;
}

单目操作符函数重载

-,~,!,&,*,->,.

成员函数

对象.operator#(void)
const 类 operator#(void)
{

}

全局函数

const 类& operator#(const 类& that)
{

}

前++/–

类& operator#(void)
{
	
}
类& operator#(类& that)
{
	
}
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	/*Point& operator++(void)
	{
		++x;
		++y;
		return *this;
	} // 成员函数
	*/
	friend Point& operator++(Point& that);
};
Point& operator++(Point& that)
{
	that.x++;
	that.y++;
	return that;
}

后++/–

const 类& operator#(int)
{
	
}
const 类& operator(类& that,int)
{
	
}
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	/*Point operator++(int)
	{
		Point temp(x,y);
		x++;
		y++;
		return temp;
	} // 成员函数,int为哑元
	*/
	friend Point operator++(Point& that,int);
};
Point operator++(Point& that,int)
{
	Point temp(that.x,that.y);
	that.x++;
	that.y++;
	return temp;
}

输入输出操作符重载

1、cout是ostream类型的对象,cin是istream类型的对象
2、如果输入输出运算符为成员函数,那么调用者应该是ostream/istream,而我们无权增加标准库的代码,因此输入/输出运算符只能定义为全局函数
注意:在输入输出过程中,cin/cout会记录错误标志,因此不能加const属性

ostream& operator<<(ostream& os,const 类& p)
{
	
}
istream& operator>>(istream& is,类& p)
{
	
}
class Point
{
	int x;
	int y;
public:
	Point(int _x=0,int _y=0)
	{
		x = _x;
		y = _y;
	}
	friend ostream& operator<<(ostream& os,const Point& p);
	friend istream& operator>>(istream& is,Point& p);
};
ostream& operator<<(ostream& os,const Point& p)
{
	return os << p.x << "," << p.y;
}
istream& operator>>(istream& is,Point& p)
{
	cout << "请输入x的值:";
	is >> p.x;
	cout << "请输入y的值:";
	is >> p.y;
	return is;
}

特殊操作符的重载

下标操作符[]

下标操作符常用于在容器中以下标方式获取元素。

类型& operator[](int i)

函数操作符()

一个类如果重载了函数操作符,那么它的对象就可以像函数一样使用,参数的个数及返回值类型,可以不确定,它是唯一一个可以有缺省参数的操作符

解引用操作符*、成员访问操作符->

如果一个类重载了*和->,那么它的对象就可以像指针一样使用。所谓的智能指针就是一种类对象,它支持解引用和成员访问操作符。

智能指针

常规指针的缺点:

当一个常规指针离开它的作用域时,只有该指针所占用的空间会被释放,而它指向的内存空间能否被释放就不一定了,在一些特殊情况下(人为、业务逻辑的特殊)free或delete没有执行,就会形成内存泄漏。

智能指针的优点:

智能指是一个封装了常规指针的类类型对象,当它离开作用域时,它的析构函数会负责释放常规指针所指向的动态内存(以正确方式创建的智能指针,它的析构函数才会正确执行)。

智能指针和常规指针的相同点:

都支持*和->运算

不同点:

任何时候,一个对象只能使用一个智能指针来指向,而常规指针可以指向多次。
智能指针的赋值操作需要经过拷贝构造和赋值构造特殊处理(深拷贝)。
class Int
{
	int val;
public:
	Int(int val=0):val(val){}
	Int& operator=(const int val)
	{
		this->val = val;
		return *this;
	}
	~Int(void)
	{
		cout << "Int的析构函数" << endl;
	}
	friend ostream& operator<<(ostream& os,Int& n);
};
ostream& operator<<(ostream& os,Int& n)
{
	return os << n.val;
}
class IntPointer
{
	Int* ptr;
public:
	IntPointer(Int* ptr):ptr(ptr){}
	Int& operator*(void)
	{
		return *ptr;
	}
	~IntPointer(void)
	{
		delete ptr;
	}
};
int main()
{
	Int* num = new Int(100);
	IntPointer p = num;
	cout << *p << endl;
	*p = 20;
	cout << *p << endl;
}

new/delete/new[]/delete[]运算符重载

1、C++中缺省的堆内存管理器速度较慢,重载new和delete底层使用malloc/free可以提高运行速度。
2、new在失败时会发生异常,而每次使用new时为了安全都应该进行异常捕获,而重载new操作符只需要在操作符函数中进行一次错误处理即可。
3、一些占字节数比较小的类,频繁使用new,可能会产生大量的内存碎片,而重载new操作符后,可以适当的扩大申请的字节数,减少内存碎片产生的机率。
4、重载new/delete 可以记录堆内存使用的信息
5、重载delete可以检测到释放内存失败时的信息,检测到内存泄漏。

class Test
{
	void* ptr;
public:
	Test(const int val){}
	Test(void)
	{
		cout << "构造函数" << endl;
	}
	~Test(void)
	{
		cout << "析构函数" << endl;
	}
	static void* operator new(size_t size)
	{
		printf("创建堆内存%d字节\n",size);
		malloc(size);
	}
	static void operator delete(void* ptr)
	{
		cout << "释放内存" << endl;
		free(ptr);
	}
};
int main()
{
	Test* p1 = new Test;
	p1 = NULL;
	delete p1;
}

重载运算符的限制

1、不能重载的操作符

域限定符 ::
直接成员访问操作符 .
三目运算符 ?:
字节长度操作符 sizeof
类型信息操作符 typeid

2、重载操作符不能修改操作符的优先级
3、无法重载所有基本类型的操作符运算
4、不能修改操作符的参数个数操作数
5、不能发明新的操作符

关于操作符重载的建议

1、在重载操作符时,要根据操作符实际的功能和意义来确定具体参数、返回值、是否具有const属性,返回值是否是引用或者是临时对象。
2、重载操作符要合情合理(有意义),要以实际用途为前提。
3、重载操作符的意义是为了让对象的操作更简单、方便,提高代码的可读性。
4、重载操作符要与默认的操作符的功能、运算规则一致,不要出现反人类的操作。

2017-07-25 11:21:56 zhaoxd200808501 阅读数 573
  • C++语言基础视频教程

    C++语言基础视频培训课程:本课与主讲者在大学开出的程序设计课程直接对接,准确把握知识点,注重教学视频与实践体系的结合,帮助初学者有效学习。本教程详细介绍C++语言中的封装、数据隐藏、继承、多态的实现等入门知识;主要包括类的声明、对象定义、构造函数和析构函数、运算符重载、继承和派生、多态性实现等。 课程需要有C语言程序设计的基础(可以利用本人开出的《C语言与程序设计》系列课学习)。学习者能够通过实践的方式,学会利用C++语言解决问题,具备进一步学习利用C++开发应用程序的基础。

    210670 人正在学习 去看看 贺利坚

一、操作符重载的意义

  C++中重载操作符能够扩展操作符的功能,操作符重载是以函数的方式进行的。其实操作符重载的本质为用特殊形式的函数扩展操作符的功能。

二、操作符重载的语法

  重载操作符时是通过operator关键字来定义特殊的函数,其本质是通过函数重载操作符。其语法规则如下:

Type operator Sign(const Type& p1, const Type& p2)
{
    Type ret;

    return ret;
}

Sign为系统中预定义的操作符,如:+、-、*、/,等

三、操作符重载具体实现

  操作符重载函数可以是全局函数,也可以是成员函数,下面分别给出实现:
  
1.重载为全局函数
  重载为全局函数时,必须给出所有的操作数。当然,全局函数也可以作为友元函数。下面代码为通过友元函数重载复数类的“+”操作符:
  

#include <stdio.h>

class Complex 
{
    int a;
    int b;
public:
    Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }

    int getA()
    {
        return a;
    }

    int getB()
    {
        return b;
    }

    friend Complex operator + (const Complex& p1, const Complex& p2);
};

Complex operator + (const Complex& p1, const Complex& p2)
{
    Complex ret;

    ret.a = p1.a + p2.a;
    ret.b = p1.b + p2.b;

    return ret;
}

int main()
{

    Complex c1(1, 2);
    Complex c2(3, 4);
    Complex c3 = c1 + c2; // operator + (c1, c2)

    printf("c3.a = %d, c3.b = %d\n", c3.getA(), c3.getB());

    return 0;
}

运行结果如下:

这里写图片描述

  下面给出复数类“==”和“!=”操作符非友元全局函数重载:

#include <iostream>

using namespace std;

class Complex
{
    double m_a;
    double m_b;
public:
    Complex(double a = 0, double b = 0)
    {
        m_a = a;
        m_b = b;
    }
    double getA() const
    {
        return m_a;
    }
    double getB() const
    {
        return m_b;
    }
};

bool operator == (const Complex& l, const Complex& r)
{
    return (l.getA() == r.getA()) && (l.getB() == l.getB());
}
bool operator != (const Complex& l, const Complex& r)
{
    return !(l == r);
}

int main()
{
    Complex c1(1, 2);
    Complex c2(1, 2);

    cout << "(c1 == c2) : " << (c1 == c2) << endl;
    cout << "(c1 != c2) : " << (c1 != c2) << endl;

    return 0;
}

运行结果如下所示:

这里写图片描述

2.重载为成员函数
   操作符重载为成员函数时,比重载为全局操作符重载函数少一个参数(左操作数为this指针),编译器优先在成员函数中寻找操作符重载函数。下面给出复数类各操作符重载为成员函数的代码:
  
Complex.h

#ifndef _COMPLEX_H_
#define _COMPLEX_H_

class Complex
{
    double a;
    double b;
public:
    Complex(double a = 0, double b = 0);
    double getA();
    double getB();
    double getModulus();

    Complex operator + (const Complex& c);
    Complex operator - (const Complex& c);
    Complex operator * (const Complex& c);
    Complex operator / (const Complex& c);

    bool operator == (const Complex& c);
    bool operator != (const Complex& c);

    Complex& operator = (const Complex& c);
};

#endif

Complex.cpp

#include "Complex.h"
#include "math.h"

Complex::Complex(double a, double b)
{
    this->a = a;
    this->b = b;
}

double Complex::getA()
{
    return a;
}

double Complex::getB()
{
    return b;
}

double Complex::getModulus()
{
    return sqrt(a * a + b * b);
}

// 重载+操作符
Complex Complex::operator + (const Complex& c)
{
    double na = a + c.a;
    double nb = b + c.b;
    Complex ret(na, nb);

    return ret;
}

// 重载-操作符
Complex Complex::operator - (const Complex& c)
{
    double na = a - c.a;
    double nb = b - c.b;
    Complex ret(na, nb);

    return ret;
}

// 重载*(乘法)操作符
Complex Complex::operator * (const Complex& c)
{
    double na = a * c.a - b * c.b;
    double nb = a * c.b + b * c.a;
    Complex ret(na, nb);

    return ret;
}

// 重载/(除法)操作符
Complex Complex::operator / (const Complex& c)
{
    double cm = c.a * c.a + c.b * c.b;
    double na = (a * c.a + b * c.b) / cm;
    double nb = (b * c.a - a * c.b) / cm;
    Complex ret(na, nb);

    return ret;
}

// 重载==操作符
bool Complex::operator == (const Complex& c)
{
    return (a == c.a) && (b == c.b);
}

// 重载!=操作符
bool Complex::operator != (const Complex& c)
{
    return !(*this == c);
}

// 重载=(赋值)操作符
Complex& Complex::operator = (const Complex& c)
{
    if( this != &c )
    {
        a = c.a;
        b = c.b;
    }

    return *this;
}

main.cpp

#include <stdio.h>
#include "Complex.h"

int main()
{
    Complex c1(1, 2);
    Complex c2(3, 6);
    Complex c3 = c2 - c1;
    Complex c4 = c1 * c3;
    Complex c5 = c2 / c1;

    printf("c3.a = %f, c3.b = %f\n", c3.getA(), c3.getB());
    printf("c4.a = %f, c4.b = %f\n", c4.getA(), c4.getB());
    printf("c5.a = %f, c5.b = %f\n", c5.getA(), c5.getB());

    Complex c6(2, 4);

    printf("c3 == c6 : %d\n", c3 == c6);
    printf("c3 != c4 : %d\n", c3 != c4);

    (c3 = c2) = c1;

    printf("c1.a = %f, c1.b = %f\n", c1.getA(), c1.getB());
    printf("c2.a = %f, c2.b = %f\n", c2.getA(), c2.getB());
    printf("c3.a = %f, c3.b = %f\n", c3.getA(), c3.getB());

    return 0;
}

运行结果如下:

这里写图片描述

四、注意事项

  1.C++规定,赋值操作符“=”只能重载为成员函数;
  2.操作符重载不能改变原操作符的优先级
  3.操作符重载不能改变操作数的个数
  4.操作符重载不能改变操作符的原有语义

10. C++操作符重载

阅读数 84

C++中操作符重载的使用

博文 来自: fengbingchun
没有更多推荐了,返回首页