30/09/2018, 18:00

Hỏi về hàm trả về tham chiếu?

Xin chào mọi người,
Mình đang tìm hiểu về Operator và được biết khái niệm về hàm trả về tham chiếu, cụ thể khi mình viết operator << hay >> để xuất hay nhập 1 đối tượng thuộc lớp PhanSo // do mình tự định nghĩa :

Đây là khai báo ở file PhanSo.h:

friend ostream& operator << (ostream &, PhanSo);
friend istream& operator >> (istream &S, PhanSo &);

và phần cài đặt ở file PhanSo.cpp:

ostream & operator<<(ostream &os, PhanSo x)
{
	os << x.Tu << "/" << x.Mau;
	return os;
}

 istream & operator>>(istream &is, PhanSo &x)
 {
 	cout << "
Nhap vao tu so = ";
 	is >> x.Tu;

 	do 
 	{
 		cout << "
Nhap vao mau so = ";
 		is >> x.Mau;

 		if (x.Mau == 0) 
 		{
 			cout << "
Mau so phai khac 0, nhap lai mau so !";
 		};

 	} while (x.Mau == 0);

	return is;
}

Câu hỏi của mình là :

  1. Ở phần khai báo là 2 hàm trả về tham chiếu, vậy hàm trả về tham chiếu có tác dụng gì trong trường hợp này ?
  2. Ở phần cài đặt, ostream & operator<<(ostream &os, PhanSo x), os (hay is bên istream) là biến loại gì ? Có tác dụng gì ?
  3. Hàm trả về tham chiếu nên dùng trong trường hợp nào ?
    Xin cảm ơn mọi người !
Minh Hoàng viết 20:13 ngày 30/09/2018

1/ trả về tham chiếu để có thể thực hiện liên tục các phép <<. ví dụ: cout << mot << hai << ba;
2/ gần giống với một đối tượng cout (hoặc cin), nhưng mình ko biết chắc nên ko trả lời được.
3/ …

Hoàng Việt viết 20:06 ngày 30/09/2018

Tại sao trả về tham chiếu lại để thực hiện liên tục các phép gán @@ Mình không hiểu ?

Minh Hoàng viết 20:09 ngày 30/09/2018

Có 3 sự lựa chọn để return là: void, copy (ostream) và tham chiếu (ostream&).
Nếu đơn giản chỉ là xuất ra một biến thì bạn có thể chỉ cần overload:

void operator<<(ostream &os, PhanSo x);

nhưng khi bạn cần xuất ra liên tiếp các biến (cout<<mot<<hai), thì bạn cần overload:

ostream & operator<<(ostream &os, PhanSo x);

hoặc làm một cách tay chân:

cout<< mot;
cout<< hai;

tại sao lại trả về tham chiếu chứ không phải một bản copy, đó là do các đối tượng stream không thể copy được. Trong link bên dưới đây, có giải thích khá dễ hiểu về lý do đó.

stackoverflow.com
user756327

Why copying stringstream is not allowed?

c++, stringstream
asked by user756327 on 07:59PM - 15 May 11

stackoverflow.com
AlexDan

why overloading of operator<< must return by reference?

c++, overloading, operator-keyword
asked by AlexDan on 01:41PM - 10 Mar 12
Hoàng Việt viết 20:15 ngày 30/09/2018

Cảm ơn bạn về bài viết và mình có đọc 1 số định nghĩa trong 1 bài viết khác về hàm trả về tham chiếu như sau :

I. Hàm trả về tham chiếu là để tránh Copy Constructor được gọi, còn hàm trả về tham trị luôn cho gọi Copy contructor.

-> Câu này có nghĩa là gì vậy bạn ?

II. Dùng hàm trả về tham chiếu trong trường hợp kết thúc hàm mà biến trả về vẫn còn tồn tại.Cụ thể có các th sau :

a. Khi hàm là thành viên của lớp, và biến trả về là thành viên của lớp :
class C {
int x;
int& getX()
{ return x;} //hàm này trả về tham chiếu được
}

-> Nếu dùng hàm bình thường, thì vẫn trả về giá trị của biến x được ? Tại sao lại dùng tham chiếu ?

b. Khi biến được trả về có tầm vực cao hơn tầm vực của hàm :
int& processX(int& x)
{return x;} // hàm này nhận vào tham chiếu x và trả ra tham chiếu x

-> Khác nhau gì giữa tầm vực của biến và tầm vực của hàm trong trường hợp này ?

Mình thật sự rất muốn hiểu khái niệm hàm trả về tham chiếu này, nhưng thật sự vẫn còn mơ hồ quá

Minh Hoàng viết 20:14 ngày 30/09/2018

I. Hàm trả về tham chiếu là để tránh Copy Constructor được gọi, còn hàm trả về tham trị luôn cho gọi Copy contructor.

Câu này hơi dài dòng nên gây rối, bạn chỉ cần biết tham chiếu sẽ không gọi Copy Constructor (có thể hiểu nôm na là nó sẽ ko nhân bản đối tượng đó mà trả về chính đối tượng đó).

II. Dùng hàm trả về tham chiếu trong trường hợp kết thúc hàm mà biến trả về vẫn còn tồn tại.Cụ thể có các th sau :

Có lẽ là tránh tổn thất thời gian copy (và có lẽ là một chút bộ nhớ).

ở trường hợp a, việc method getX trả về một tham chiếu nhằm mục đích là cho người sử dụng method có quyền đọc-ghi giá trị x. Nếu chỉ dùng hàm trả về tham trị, thì người dùng chỉ có quyền đọc giá trị x (đây là cách đúng cho các method dạng get). Tùy vào ý định của người viết method đó.
ở trường hợp b, (tầm vực tiếng anh là scope, bạn có thể tìm hiểu theo về scope trên stackoverflow). Khi bạn truyền tham chiếu thì biến tham chiếu sẽ có scope rộng hơn scope của hàm (vì chắc chắn hàm sẽ bị hủy trước). Và để xuôi chèo thì nên trả về một tham chiếu.
ví dụ bạn mang một con gà(int& x) vào nhà hàng(method) và kêu họ làm một món ăn bằng con gà này thì nhà hàng phải sử dụng con gà này để làm món và mang ra cho bạn(return) một món ăn bằng con gà này chứ không phải một con gà khác giống y chang vậy. Tuy là 2 con gà giống như nhưng có những con có bệnh. Nhưng đó cũng tùy ý định của đầu bếp (người viết method).

Tham chiếu tức là tác động chính đối tượng đó, tham trị là tác động lên bản copy của đối tượng đó, và vì thế tham trị không thể thay đổi được đối tượng ban đầu. Nó đều giống nhau nếu bạn nhìn (get) nhưng khác nhau khi bạn tác động lên nó (set).
Việc sử dụng trả về tham chiếu hay không đều do mục định của người method đó, trừ một số trường hợp chỉ có thể sử dụng cách này hay cách kia như những vẫn đề ở đầu topic. Việc này thì có lẽ phải trải nghiệm và học hỏi nhiều, mình không có nhiều kinh nghiệm lắm nên câu hỏi số 3 mình ko dám trả lời.

p/s: Mình mô tả hơi khó hiểu, có gì bạn cứ hỏi tiếp, đây cũng là cơ hội để mình ôn lại kiến thức

Hoàng Việt viết 20:05 ngày 30/09/2018

Bạn có nói hàm trả về tham chiếu được sử dụng tùy theo mục đích của người sử dụng : quyền đọc - ghi. Phải chăng vd này của mình có đúng với bạn nói không ? :

#include <iostream>
using namespace  std;

int trave1(int &x)
{
        x+= 2
	return x;
}

int & trave2(int &x)
{
        x+= 2
	return x;
}

int main()
{
	int a = 3;
	trave1(a); // a = 5
	trave2(a) = 2; // a = 2
	cout << a;
	system("pause");
	return 0;
}

Mình hiểu là hàm trả về tham chiếu sẽ trả về đối tượng ban đầu, và đối tượng đó có thể set = 1 giá trị khác, còn hàm trả về tham trị thì không thể, do trả về 1 bản copy của đối tượng đó. Nhưng mình thắc mắc thế này : Nếu tham số truyền vào là tham chiếu rồi : hàm trave2 trả về tham chiếu của chính nó thì hàm trave1 trả về giá trị gì ? Cảm ơn bạn nhiều !

Minh Hoàng viết 20:02 ngày 30/09/2018

Bạn có thể xem thêm về lvalue và rvalue. Hàm trave1 sẽ trả về một giá trị rvalue. Còn trave2 sẽ trả về một giá trị lvalue.

stackoverflow.com
Pranit Kothari

Exact difference between rvalue and lvalue

c++, c++11, rvalue
asked by Pranit Kothari on 05:37AM - 28 Jun 13

Hoặc có một định nghĩa ở đây về lvalue và rvalue:

lvalue - an object that occupied some identifiable location in memory.
rvalue - any object that is not lvalue.


còn hàm trả về tham trị thì không thể, do trả về 1 bản copy của đối tượng đó

Đúng, chúng ta chỉ có thể set trên bản copy, và như vậy thì bản gốc ko bị ảnh hưởng gì.

p/s: một bài viết trên blog của bạn mình https://yaphats.wordpress.com/2015/08/24/the-big-five-in-c/

truong cong hoai bao viết 20:11 ngày 30/09/2018

float &operator [] (int i); tai sao dùng tham chiếu ở đây anh?

Bài liên quan
0