友元、内联和宏

本文介绍了C++中的友元、内联和宏的概念与使用。友元函数和友元类允许非成员函数访问类的私有成员,但可能破坏封装性。内联函数用于优化小函数的调用效率,而宏则在预处理阶段进行代码替换,但宏可能导致类型检查缺失和代码膨胀问题。内联通常比宏更安全。
摘要由CSDN通过智能技术生成
友元

采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的成员函数,但又需要频繁地访问类的私有数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

友元函数
友元函数可以直接访问类的私有成员,在类外定义,不属于任何类。

这里写图片描述

如上图所示,将输出流重载,使其可以输出我们自定义类型的对象。有两种做法,

第一:将输出运算符重载为类的成员函数,这种写法如下:

    ostream& operator<<(ostream& os)
    {
        os << "_c = " << _c << endl;
        os << "_i = " << _i;
        return os;
    }

由于是类的成员函数,故只需要一个参数,它的调用方式为:a.operator<<(cout);,虽然实现了功能,但与我们平常的用法习惯不一样。
第二种:在类外部定义该函数,并将其声明为类的友元函数,这种方式,其使用法和我们平常的用法习惯相同。

友元类
如果一整个类的函数需要访问另一个类的私有成员,则可以将该类声明为另一个类的友元类。

class A{
public:
    friend class B;//声明B为A的友元类
    A(char c, int i)
        :_c(c)
        , _i(i)
    {}
private:
    char _c;
    int _i;
};

class B{
public:
    B(char c, int i, float f)
        :_a(c,i)
        , _f(f)
    {}
    void Print()
    {
        cout << "_c = " << _a._c << endl;
        cout << "_i = " << _a._i << endl;
        cout << "_f = " << _f << endl;
    }
private:
    float _f;
    A _a;
};

测试如下:
这里写图片描述

友元关系在一定程度上破坏了类的封装性,所以不要滥用友元。

内联

考虑下面这个函数:

int Max(int a, int b)
{
    return a>b ? a : b;
}

为这样的小操作定义一个函数的好处是:

  • 阅读函数理解该函数要做什么比直接看表达式代码要容易的多;
  • 如果程序中有 200处 调用了该函数,则需要修改其功能时,修改一份实现比修改 200 处的代码要简单容易得多;
  • 每处的调用可以保证其实现是相同的;
  • 函数可以被重用,无需在每个需要的地方编写实现代码。

看起来,这样的函数是完美无瑕的,可实际上并不如此:

函数的调用不但要维护函数的 栈帧空间,保存上下文环境,进行指令跳转,还要进行参数的拷贝(在不使用引用传参的情况下),当参数较多较大时,函数调用的开销比直接的表达式代码大得多。

C++ 中 inline 关键字正好可以解决这一点。若一个函数被定义为内联函数,则在编译时,该函数将在程序的每个调用点“内联的”展开,也就是说,函数调用会被替换为函数体的代码。比如上述的函数若被定义为内联函数:

inline int Max(int a, int b)
{
    return a>b ? a : b;
}

则: int ret = Max(10, 20); 将被替换为 int ret = 10>20 ? 10 : 20; 这种方式避免了函数调用的开销。

但并不见得所有的内联函数都适合在调用点上展开,当一个函数包含 200 行代码时,若在每个调用点展开,会增大程序体积,使程序变得无比臃肿;如果一个递归的函数在调用点处被展开,那样的结果不敢想象。内联只是对编译器的一个建议,编译器可以选择忽略它,内联机制用于优化较小的、没有循环、递归结构的、被频繁调用的函数

对于类的成员函数,默认情况下都是内联函数,当然也只是建议,对于较大的成员函数,即使它被一个隐式的 inline 修饰,它也不会成为内联函数。

宏与内联对比

C++ 中的内联机制提供了比宏更安全功能,下面来看看宏和内联有什么区别。

  • 宏在预处理期有预处理器进行替换,内联则是在编译阶段将函数体展开;
  • 内联函数的参数类似于函数传参,会进行类型检查,而宏的参数不会进行类型检查;
  • 内联比宏安全得多,看看下面的代码:
#define Square(a) a*a

如果这样调用似乎没什么问题:

Square(10);//10*10 = 100

但如果这样调用的话,就会出现问题:

Square(10+5);//10+5*10+5 = 65

与我们期望的的 15*15=225 的结果大相径庭。那如果把宏改为下面这种是不是就行了呢?

#define Square(a) (a)*(a)

括号保证 a 作为一个整体被计算,这样似乎没有问题了,实际上,这个宏仍然存在很大的缺陷,我们如果这样调用它:

Square(++i);

我们期望 i 被加1,但实际上调用该宏函数后,i 的值被加了两次。宏既然有这么多的缺点,我们最好用内联、const代替宏

  • 宏不能调试,因为在预处理阶段,宏已经被替换。

【果冻 http://blog.csdn.net/jelly_9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值