简体   繁体   English

在c ++中使用友元函数进行I / O运算符重载

[英]Using friend functions for I/O operator overloading in c++

I am learning c++ on my own. 我自己学习c ++。 I was studying operator overloading, i was able to understand addition and subtraction operator overloading. 我正在研究运算符重载,我能够理解加法和减法运算符重载。 But overloading of I/O operators is a bit confusing. 但I / O运算符的过载有点令人困惑。 I have created a class for Complex numbers, now i am overloading operators. 我已经为复数创建了一个类,现在我正在重载运算符。

Function prototype from Complex.h Complex.h中的函数原型

friend ostream& operator<<(ostream&, const Complex&);

Function from Complex.cpp Complex.cpp的功能

ostream& operator<<(ostream& os, const Complex& value){
os << "(" << value.r <<", "
<< value.i << ")" ;
return os;
}
  1. Can anyone explain (on a basic level) why we have to use a friend function declaration here? 任何人都可以解释(在基本层面上)为什么我们必须在这里使用朋友函数声明?
  2. Why do we have to pass all arguments and the return type of the operator by reference? 为什么我们必须通过引用传递所有参数和运算符的返回类型?
  3. This function works fine without using const, but why are we using const here? 这个函数在不使用const的情况下工作正常,但为什么我们在这里使用const? What is the advantage of passing Complex as a constant reference? 将Complex作为常量引用传递的优点是什么?

You do not have to make the streaming operator a friend. 不必使流运营商的朋友。 It does have to be externally declared as the Complex object is on the right-hand side of the operator. 必须在外部声明为复杂的对象是对操作者的右手边。

However if your Complex class has a way to access the members required (possibly through getters) you could get the streaming operator to use them. 但是,如果您的Complex类有一种方法来访问所需的成员(可能通过getter),您可以让流操作符使用它们。 Say: 说:

std::ostream& operator<<( std::ostream& os, Complex const& cpx )
{
   return os << cpx.getReal() << ',' << cpx.getImaginary();
}

Your operator/ overload can be done as an internal function but actually is better implemented also as an external function with two const& parameters. 您的operator/重载可以作为内部函数完成,但实际上也可以更好地实现为具有两个const和参数的外部函数。 If it is a member function it needs to be a const-member. 如果它是一个成员函数,它需要是一个const成员。 Yours is not. 你的不是。

You might implement it based on operator /= thus 您可以基于operator /=实现它

Complex operator/ ( Complex const& lhs, Complex const& rhs )
{
    Complex res( lhs );
    res /= rhs; // or just put return in front of this line
    return res; 
}

For friend ostream& operator<<(ostream&,const Complex&); 对于friend ostream& operator<<(ostream&,const Complex&); :

  1. Because you declare a free function here, and would like it to access the internals (private/protected) of your Complex objects. 因为您在此处声明了一个自由函数,并希望它访问Complex对象的内部(私有/受保护)。 It is very common to have "friend free functions" for those overloads, but certainly not mandatory. 对于那些重载具有“朋友自由功能”是很常见的,但肯定不是强制性的。

  2. Because streams are non copyable (it does not make sense, see also this post ), passing by value would require a copy. 因为流是不可复制的(它没有意义,请参阅这篇文章 ),传递值需要一个副本。 Passing Complex by value would also require a useless copy. 按值传递Complex也需要无用的副本。

  3. Because those output operators are not supposed to modify the objects they are working on (Input operators are, obviously), so add const to ensure that. 因为那些输出操作符不应该修改它们正在处理的对象(显然是输入操作符),所以添加const来确保它。

  1. Can anyone explain (on a basic level) why we have to use a friend function declaration here? 任何人都可以解释(在基本层面上)为什么我们必须在这里使用朋友函数声明?

    If you declare a class friend of another class, then you are saying that the friend class can access the private and protected attributes and functions of your class. 如果你声明了另一个类的类friend of ,那么你就是说友元类可以访问你的类的私有和受保护的属性和函数。
    In your case you declare ostream& operator<< as friend which means that within the body of that function the ostream class will be able to access private and protected functions and attributes of your complex class. 在你的情况下,你声明ostream& operator<< as friend,这意味着在该函数的主体内,ostream类将能够访问复杂类的私有和受保护的函数和属性。

  2. Why do we have to pass all arguments and the return type of the operator by reference? 为什么我们必须通过引用传递所有参数和运算符的返回类型?

    Because it was written like that to avoid to make a copy of the ostream object. 因为它是这样写的,以避免制作ostream对象的副本。 So your << overload will use the same object without making a copy each time you use it. 所以你的<<重载将使用相同的对象,而不是每次使用它时都复制。

  3. This function works fine without using const, but why are we using const here? 这个函数在不使用const的情况下工作正常,但为什么我们在这里使用const? What is the advantage of passing Complex as a constant reference? 将Complex作为常量引用传递的优点是什么?

    const after the function means that you are not changing any attributes of the class, which you dont. const之后的函数意味着你没有改变类的任何属性,你不这样做。

I think that is about right and i hope it helps, but if anyone wants to comment feel free. 我认为这是正确的,我希望它有所帮助,但如果有人想发表意见,请随意。

friend ostream& operator<<(ostream&, const Complex&);

Here the function needs be friend because you may need to be able to access the private member of complex. 这里的功能需要是朋友,因为您可能需要能够访问复杂的私有成员。

When you overload << for Complex class it is likely that you will need to access the data members of Complex class in the overloaded functions and the data members may be private. 当你重载<< for Complex类时,很可能你需要在重载函数中访问Complex类的数据成员,而数据成员可能是私有的。 In case they are private, to access the data members in operator << you will need to make it a member of complex class which is not possible since the left side object of operator is of ostream class type so only option is to make it a friend of Complex and overlaod << using global scope function. 如果它们是私有的,要访问operator <<的数据成员,你将需要使它成为complex类的成员,这是不可能的,因为运算符的左侧对象是ostream类类型所以只有选项才能使它成为Complex朋友和重叠<<使用全局范围功能。

Now the object is returned by reference to avoid creation of multiple copy of the object as told by others. 现在,通过引用返回对象,以避免创建其他人告诉的对象的多个副本。 And the object has to be returned because it is going to support when the operator is using for cascaded write like `cout< 并且必须返回该对象,因为当操作符用于像cout <的级联写操作时它将支持

The argument is passed as a const because it in always a good practice to receive the object as const if it is not going to be changed in side the function. 该参数作为const传递,因为如果它不会在函数的一侧被更改,那么将对象作为const接收总是一个很好的做法。 This makes it work on even const objects. 这使它甚至可以在const对象上工作。

1.Can anyone explain(on a basic level) that why do we have to use friend function here? 1.任何人都可以解释(基本层面)为什么我们必须在这里使用友方功能?

If you need to access private member of Complex in operator<< . 如果您需要访问operator<<Complex私有成员。

2.Why do we have to pass operator by reference? 2.为什么我们必须通过引用传递操作符?

For efficiency. 为了效率。 Passing by const reference is faster than passing by value for user defined type usually. 通过const引用传递比通常传递用户定义类型的值更快。

3.This function works fine without using const, but why are we using const here? 3.这个函数在不使用const的情况下工作正常,但为什么我们在这里使用const? What is the advantage of passing Complex as a constant object? 将Complex作为常量对象传递的优点是什么?

If you don't do so, you can't output a const object, such as: 如果不这样做,则无法输出const对象,例如:

const Complex c;
std::cout << c;

For non-friend member function operators, there is only one argument, the right-hand side of the operator. 对于非朋友成员函数运算符,只有一个参数,即运算符的右侧。 So for your example with the division operator you can do like this: 因此,对于使用除法运算符的示例,您可以这样做:

Complex a = ...;
Complex b = ...;

Complex c = a / b;

and the compiler will actually see the last as 并且编译器实际上会将最后一个视为

Complex c = a.operator/(b);

This means that for eg the output operator, if it's a normal member function you "output" to an instance of the class. 这意味着对于例如输出操作符,如果它是普通成员函数,则“输出”到类的实例。 in other words it would be used like 换句话说,它将被用作

Complex a = ...;
Complex b = ...;
a << b;  // Really a.operator<<(b);

Clearly this is not what's wanted when implementing an operator that should output to a stream. 显然,当实现应输出到流的运算符时,这不是所需的。 Therefore we need to use a non-member function, which can take two different arguments. 因此,我们需要使用非成员函数,它可以使用两个不同的参数。


For the second point, remember that the default method of passing arguments is by value , which means that the values are copied . 对于第二点,请记住传递参数的默认方法是按值 ,这意味着复制值。 For smaller classes it doesn't matter, but if there's a lot of data, or the class has a complex copy-constructor, then that might incur quite a performance hit. 对于较小的类而言并不重要,但如果有大量数据,或者类具有复杂的复制构造函数,则可能会产生相当大的性能损失。 Also, some types and classes can't be copied, for example std::istream so you have to pass them by reference. 此外,某些类型和类不能被复制,例如std::istream因此您必须通过引用传递它们。


For the last point, by making some arguments const you tell both the compiler and the user of the function that it will not change the argument. 最后一点,通过创建一些const参数,告诉编译器和函数的用户它不会改变参数。 Besides making it easier for users of the function to know that calling the function will not have any side-effects, it will also allow the compiler to possibly do some optimizations. 除了让函数用户更容易知道调用函数不会产生任何副作用外,它还允许编译器进行一些优化。


Generally regarding operator overloading, you might want to read this reference on the subject . 通常关于运算符重载,您可能希望阅读有关该主题的此参考

Can anyone explain why we have to use friend function here? 谁能解释为什么我们必须在这里使用友方功能?

A friend of a class can access private or protected variables/functions from the class in which it is specified as a friend . 一个friend的一类可以访问privateprotected从它被指定为一个类变量/函数friend It is necessary to make the I/O operators a friend of class Complex if they need to access any private functionality. 如果需要访问任何私有功能,则必须使I / O运算符成为Complex类的friend If not, one may simply define the function outside of the class without declaring it as such. 如果没有,可以简单地在类之外定义函数而不声明它。

Why do we have to pass operator by reference? 为什么我们必须通过引用传递操作符?

I'm not sure what you mean. 我不确定你是什么意思。 The operator is a function we are defining, not a parameter that is being passed anywhere. 运算符是我们定义的函数,而不是在任何地方传递的参数。 Are you referring to the parameters that it takes? 你指的是它需要的参数吗? Well, std::ostream is taken as a reference type because taking it by value would cause a copy, something which stream classes do not allow. 好吧, std::ostream被当作​​引用类型,因为按值获取它会导致副本,这是流类不允许的。 Try it and you will get a compiler error. 试试看,你会遇到编译错误。 I will touch on why Complex is taken as a reference type below. 我将讨论为什么将Complex视为下面的参考类型。

This function works fine without using const , but why are we using const here? 这个函数在不使用const情况下工作正常,但为什么我们在这里使用const What is the advantage of passing Complex as a constant object? Complex作为常量对象传递的优点是什么?

First of all, why not use const ? 首先,为什么使用const The const type modifier is a clear statement to the compiler that we will not change the object in any way. const类型修饰符是编译器的一个明确声明,我们不会以任何方式更改对象。 It also makes it clear to the maintainers of your code. 它还使维护者清楚了解您的代码。 Besides, writing out an object typically requires the value, meaning no modifications to the object are necessary. 此外,写出对象通常需要该值,这意味着不需要对对象进行任何修改。

Using a reference to const also allows you to pass rvalues or tempoaries to the function, which are not possible with a reference to non - const , which only take lvalues. 使用对const的引用还允许您将rvalues或tempoaries传递给函数,这对于 const的引用是不可能的,它只接受lvalues。 For example: 例如:

struct S { };

void f(S&);
void h(S const&);

int main()
{
    S s;

    f(s); // OK
    f(S()); // ERROR!

    h(s); // OK
    h(S()); // OK
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM