4 参数传递与返回值

4 参数传递与返回值

构造函数 ctor 被放在 private 区域

继续看之前的代码

class complex
{
    public:
        complex (double r = 0, double i = 0)
            : re (r), im (i)
        {}
        complex& operator += (const complex&);
        double real() const
        {
            return re;
        }
        double imag() const {return im;}
    private:
        double re, im;

        friend complex& __doapl (complex*, const complex&);
};

如果我们把构造函数像这样放到 private 区域会怎么样 ?

class complex
{
    public:

        complex& operator += (const complex&);
        double real() const
        {
            return re;
        }
        double imag() const {return im;}
    private:
        double re, im;

        complex (double r = 0, double i = 0)
            : re (r), im (i)
        {}

        friend complex& __doapl (complex*, const complex&);
};

于是我们就无法创建对象了, 因为我们需要构造函数来创建对象, 但是它被放到了私有区域, 外界无法调用, 自然就无法创建对象了

当然也有构造函数被放到私有区域的情况

ctors 放在 private 区

class A
{
    public:
        static A& getInstance();
        setup()
        {
            //...
        }
    private:
        A();
        A(const A& rhs);
        //...
};

A& A::getInstance()
{
    static A a;
    return a;
}

这个是一个设计模式 Singleton, 单子模式, 它就是把构造函数放在了私有区域

我只允许 A 创建一个对象

外界调用要用这种形式才能调用这个函数, 来创建对象

A::getInstance().setup();

const member functions 常量成员函数

class complex
{
    public:
        complex(double r = 0, double i = 0)
            :   re(r), im(i)
        {}

        complex& operator += (const complex&);
        double real() const { return re; }
        double imag() const { return im; }

    private:
        double re, im;

        friend complex& __doapl(complex*, const complex&);
};

现在我们看这俩函数

double real() const { return re; }

double imag() const { return im; }

这一件事情很多人都会忽略掉, 就是在函数的后面加上 const, 小括号后面大括号前面.

我们看一下这两个函数的功能, 只是取出来实部和虚部, 并没有改变变量的值

类里面的函数有会改变数据的, 和不改变数据两种函数

不会改变数据内容的函数, 加上 const, 它的意思就是这个函数不会改变数据;

如果不加的话, 使用者如果这样调用函数

{
    const complex c1(2, 1);
    cout << c1.real();
    cout << c1.imag();
}

编译器就会报错, 因为使用者是把 c1 当作一个常量的, 然后你类里面的函数没写 const, 就是说这个函数可能会改变数据, 然后编译器就会报错

参数传递 : pass by value vs. pass by reference(to const)

class complex
{
    public:
        complex(double r = 0, double i = 0)
            :   re(r), im(i)
        {}
        complex& operator += (const complex&);
        double real() const { return re; }
        double imag() const { return im; }
    private:
        double re, im;

        friend complex& __doapl(complex*, const complex&);
};

上面函数的参数有的是直接一个变量, 有的是后面有 &, 有的是有 const 又有 &

pass by value, 就是这个 value 整个都传进去, 压到栈里面

有时候整个传进去, 会占用很多内存, 太大了. 这时候我们想起来以前 C 语言可以传指针, 现在 C++ 有一个东西像指针但是更漂亮, 这就是 reference 引用.

一个良好的习惯就是最后所有的参数传递都是传引用

如果你希望是传过去很快, 但是不希望你改, 那你就可以在引用前面加 const, 像下面这样

ostream&
operator << (ostream& os, const complex& x)
{
    return os << '(' << real(x) << ',' << imag(x) << ')';
}

返回值传递 : return by value vs return by reference (to const)

reference 除了参数传递, 还有个地方就是返回值传递

函数前面就是返回值类型

尽量用引用的意思是不是所有的情况都可以用它

引用类型

friend (友元)

朋友可以来拿数据

就是这个朋友可以拿这个类的私有成员

友元

相同 class 的各个 object 互为 friends (友元)

class complex
{
    public:
        complex (double r = 0, double i = 0)
            :   re(r), im(i)
        {}

        int func(const complex& param)
        {
            return param.re + param.im;
        }
    private:
        double re, im;
};
{
    complex c1(2, 1);
    complex c2;

    c2.func(c1);
}

现在我们增加了这么一个函数 func, 它接收一个复数, 取得传进来这个复数的实部和虚部, 注意, 这里是传进来另外一个复数, 它竟然可以直接拿私有的实部和虚部, 也没有出现 friend 字眼

有很多个角度可以去解释

相同 class 的各个 object 互为 friends (友元), 这句话可以解释, 你可能会有其它角度来解释, 但这句话可能比较好一些

class body 外的各种定义 (definitions)

  • 什么情况下可以 pass by reference

  • 什么情况下可以 return by reference
    局部变量局部对象, 就不能返回引用, 因为函数一结束, 他们就会随着栈内存释放掉了

do assignment plus __doapl

inline complex&
__doapl(complex* ths, const complex&)
{
    ths->re += r.re;    // 第一个参数将会被改
    ths->im += r.im;    // 第二个参数不会被改
    return *ths;
}

inline complex&
complex::operator += (const complex& r)
{
    return __doapl(this, r);
}

现在的情况是 ths 和 r 相加以后, 结果放到 ths 里面去, 然后 ths 本来就存在嘛, 是传进来的, 所以这里就可以返回引用类型的返回值