文章目录
1. 运算符重载
此处我们建立一个Test类来对其进行相应的运算符重载。
class Test{
public:
explicit Test(int data ,char tmp = '0') : m_data(data)
{ }
Test(const Test &t)
{
m_data = t.m_data;
}
~Test(){
cout << "free to object: " << this << endl;
}
Test& operator=(const Test& t)
{
if(this != &t)
{
m_data = t.m_data;
}
}
Test* operator&()
{
return this;
}
const Test *operator&() const
{
return this;
}
private:
int m_data;
};
1.1 重载 +、-、*、/、% 运算符
见代码:
//+、-、*、/、%运算符重载
Test operator+(const Test& t)
{
return Test(m_data+t.m_data);
}
Test operator-(const Test& t)
{
return Test(m_data-t.m_data);
}
Test operator*(const Test& t)
{
return Test(m_data*t.m_data);
}
Test operator/(const Test& t)
{
if(t.m_data==0)
{
cout << "被除数为0" << endl;
exit(-1);
}
return Test(m_data/t.m_data);
}
Test operator%(const Test& t)
{
if(t.m_data==0)
{
cout << "被模数为0" << endl;
exit(-1);
}
return Test(m_data%t.m_data);
}
1.2 重载 +=、-= 运算符
见代码:
// += 、 -= 重载
Test& operator+=(const Test& t)
{
m_data += t.m_data;
return *this;
}
Test& operator-=(const Test& t)
{
m_data -= t.m_data;
return *this;
}
1.3 重载 前++、后++ 运算符
这里需要区分的是前++,因为是先++然后再进行相应的操作,因此可以返回其对象的引用,因此其重载声明为Test& operator++()
,而后++,是先返回当前对象,然后对其进行++操作,,注意不能返回对象的引用,因此重载的声明为Test operator++(int)
。
见代码:
//前++、后++
Test& operator++()
{
m_data++;
return *this;
}
//后++
Test operator++(int)
{
Test t(*this);
m_data++;
return t;
}
1.4 重载 <、>=、>、<= 运算符
见代码:
bool operator<(const Test& t)
{
return m_data < t.m_data;
}
bool operator>=(const Test& t)
{
return !(*this < t);
}
bool operator>(const Test& t)
{
return m_data > t.m_data;
}
bool operator<=(const Test& t)
{
return !(*this > t);
}
1.5 重载 >> 、<< 运算符
运算符>> 和 << 的重载需要用到友元函数的概念,这里先给出代码,关于友元函数的概念会在后面进行讲解
class Test{
...
//cout、cin重载
friend ostream& operator<<(ostream& out,const Test& t);
friend istream& operator>>(istream& in,Test& t);
...
};
ostream& operator<<(ostream& out,const Test& t)
{
out << t.m_data ;
return out;
}
istream& operator>>(istream& in, Test& t)
{
in >> t.m_data;
return in;
}
扩展:
我们都知道代码Test t = 10
是正确的,,相当于t(10)
,其中发生了强制类型转换(隐式的),相当于t = (Test) 10
(显示的),如果构造函数中加上了explicit关键字,那就只能显示的进行转换。但是,如果语句是int value = t
呢?
这时候就会发生错误,可能有的人会说,对其也进行强转不就行了吗,相当于int value = (int) t
,但是这是错误的,一个基本数据类型转换为对象是可行的,但是一个对象要强转为基本数据类型是不行的,因为你不知道对象中都包含哪些数据,(对象是按内存对齐的方式存储的,如果强行转换,可能会导致数据丢失,或者访问非法的内存,这些都是编译器所不允许的)。
那么该如何解决呢?
解决1:取对象的地址,将该地址进行强制,即将一个Test * 类型的指针强制转换为一个 int *类型的指针
Test* tm = &t; int * value = (int *)tm;
但是这样还是存在着问题,如果该类只有一个成员变量,那就是没有问题的,但如果是多个,就只会输出该对象中前4个Byte(int的大小),其余不输出。
解决2:重载 int 类型即可
void operator int() { return m_data; }
这种方法是最好的,能够充分的解决该问题。
2. static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化。
特性:
- 静态成员为所有类对象所共享,不属于某个具体的实例
- 静态成员变量必须在类外定义,定义时不添加static关键字
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
- 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
静态的方法只能调用静态的成员变量,不能调用普通的方法、普通成员变量,而非静态的成员函数是可以调用静态的成员函数的。
碎片知识:只有C++11才支持非静态成员变量在声明时进行初始化赋值,但是要注意这里不是初始化,这里是给声明的成员变量缺省值。
static性质总结:
- static 局部变量:首先系统会自动将其初始化为0,并且该变量存储于程序的全局数据段中,即使函数返回,它的值也不会改变,但是它的作用域会随函数的结束而结束。
- static全局变量:静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
- static普通函数:和全局变量一样,该函数只能在声明它的文件中可见,其他文件不能引用该函数
- static成员变量:上面以及提到过了
- static成员函数:与静态成员变量相似,它属于整个类而不是具体的某一个对象,并且它是没有this指针的。
3. 友元函数、友元类
友元分为:友元函数和友元类。
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
3.1 友元函数
上面的运算符重载小节中,我们对运算符<<、>>进行重载时就采用了友元函数的方法进行的,但是为什么要偏偏对这两个运算符使用友元函数来进行重载呢?
解答:因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>同理。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
注意:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用和原理相同
扩展:
假设现在Test类实例化出三个对象 t 、t1 、t2 ;若求 t = t1 + t2 ,是正常调用我们上面所实现的+运算符即可,但若是要实现 t = t1 + 10 或者 t = 10 + t2,上面所重载的+运算符就不能调用了,会发生错误,针对于该情况,对于前面那个或许还能在类中再重载一个 + 运算符,但是对于后面的就不行了,因为20不是一个Test类的对象,this指针就传不过去,因此,两者都最好采用友元函数的形式来重载。
见代码:
class Test{
...
friend Test operator+(const Test& t,int m);
friend Test operator+(int m,const Test& t);
...
};
Test operator+(const Test& t, int m)
{
return Test(t.m_data+m);
}
Test operator+(int m,const Test& t)
{
return Test(m+t.m_data);
}
3.2 友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元关系是单向的,不具有交换性。
- 友元关系不能传递,如果B是A的友元,C是B的友元,则不能说明C时A的友元。
4. 内部类
概念
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系