1.运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
一个赋值运算符重载的例子:
#include<iostream>
#include<string>
using namespace std;
class MyStr
{
private:
char *name;
int id;
public:
MyStr() {}
MyStr(int _id, char *_name) //constructor
{
cout << "constructor" << endl;
id = _id;
name = new char[strlen(_name) + 1];
strcpy_s(name, strlen(_name) + 1, _name);
}
MyStr(const MyStr& str)
{
cout << "copy constructor" << endl;
id = str.id;
if (name != NULL)
delete[] name;
name = new char[strlen(str.name) + 1];
strcpy_s(name, strlen(str.name) + 1, str.name);
}
MyStr& operator =(const MyStr& str)//赋值运算符
{
cout << "operator =" << endl;
if (this != &str)
{
if (name != NULL)
delete[] name;
this->id = str.id;
int len = strlen(str.name);
name = new char[len + 1];
strcpy_s(name, strlen(str.name) + 1, str.name);
}
return *this;
}
~MyStr()
{
delete[] name;
}
};
int main()
{
MyStr str1(1, "hhxx");
cout << "====================" << endl;
MyStr str2;
str2 = str1;
cout << "====================" << endl;
MyStr str3 = str2;
return 0;
}
参数: 一般的运算符重载函数参数是所在类的const类型的引用,用引用是因为可以避免在函数调用时对实参的一次拷贝。加const是因为我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。加上const,对于const的和非const的实参,函数就能接受;如果不加,就只能接受非const的实参。
返回值:一般返回被赋值对象的引用,也就是*this,这样可以避免返回时的一次拷贝,提高了效率。
调用时机:在用已经创建的对象进行复制时就会调用如上面代码所示MyStr str2;
str2 = str1;
当程序没有显式地提供一个以本类或本类的引用为参数的赋值运算符重载函数时,编译器会自动生成这样一个赋值运算符重载函数。
赋值运算符重载函数只能是类的非静态的成员函数
C++规定,赋值运算符重载函数只能是类的非静态的成员函数,不能是静态成员函数,也不能是友元函数。其实,之所以不是静态成员函数,是因为静态成员函数只能操作类的静态成员,不能操作非静态成员。如果我们将赋值运算符重载函数定义为静态成员函数,那么,该函数将无法操作类的非静态成员,这显然是不可行的。
注释:复制运算符重载函数不能被重载,这个以后再说。
const成员函数:
const成员函数:在成员函数后加const,const修饰this指针指向的对象,这也就保证调用这个const成员函数的对象在内部不会被改变。
如果要声明一个const类型的类成员函数,只需要在成员函数列表后加上关键字const, 例如:
class Screen {
public:
char get() const;
};
在类体之外定义const成员函数时,还必须加上const关键字,例如:
char Screen :: get() const {
return _screen[_cursor];
}
若将成员函数声明为const,则不允许通过其修改类的数据成员。 值得注意的是,如果类中存在指针类型的数据成员即便是const函数只能保证不修改该指针的值,并不能保证不修改指针指向的对象。例如:
class Name {
public:
void setName(const string &s) const;
private:
char *m_sName;
};
void setName(const string &s) const {
m_sName = s.c_str(); // 错误!不能修改m_sName;
for (int i = 0; i < s.size(); ++i)
m_sName[i] = s[i]; // 不好的风格,但不是错误的
}
const成员函数可以被对应的具有相同形参列表的非const成员函数重载,
例如:
class Screen {
public:
char get(int x,int y);
char get(int x,int y) const;
};
int main()
{
const Screen cs;
Screen cc2;
char ch = cs.get(0, 0); // 调用const成员函数
ch = cs2.get(0, 0); // 调用非const成员函数
}
const成员函数可以访问非const对象的非const数据成员,const数据成员,也可以访问const对象内的所有数据成员;
非const成员函数只可以访问非const对象的任意的数据成员(不能访问const对象的任意数据成员)
总结:
1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
3)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。
4)如果只有const成员函数,非const对象是可以调用const成员函数的。当const版本和非const版本的成员函数同时出现时,非const对象调用非const成员函数。