C++:运算符重载、static、友元函数和友元类、内部类


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(外部类)=外部类,和内部类没有任何关系
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值