Helpex - Trao đổi & giúp đỡ Đăng nhập
69

Trong khi tái cấu trúc một số mã, tôi đã bắt gặp một số phương thức getter trả về chuỗi std ::. Một cái gì đó như thế này chẳng hạn:

class foo
{
private:
    std::string name_;
public:
    std::string name()
    {
        return name_;
    }
};

Chắc chắn các getter sẽ trở lại tốt hơn const std::string&? Phương pháp hiện tại đang trả về một bản sao không hiệu quả. Thay vào đó sẽ trả về một tham chiếu const thay vì gây ra bất kỳ vấn đề?

69 hữu ích 5 bình luận 95k xem chia sẻ
12 trả lời 12
55

Cách duy nhất điều này có thể gây ra vấn đề là nếu người gọi lưu trữ tham chiếu, thay vì sao chép chuỗi và cố gắng sử dụng nó sau khi đối tượng bị hủy. Như thế này:

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName;  // error! dangling reference

Tuy nhiên, vì hàm hiện tại của bạn trả về một bản sao, nên bạn sẽ không phá vỡ bất kỳ mã hiện có nào.

55 hữu ích 5 bình luận chia sẻ
28

Trên thực tế, một vấn đề khác cụ thể với việc trả về một chuỗi không phải bằng tham chiếu, đó là thực tế std::stringcung cấp quyền truy cập thông qua con trỏ đến nội bộ const char*thông qua phương thức c_str () . Điều này đã gây ra cho tôi nhiều giờ gỡ lỗi đau đầu. Chẳng hạn, giả sử tôi muốn lấy tên từ foo và chuyển nó cho JNI để sử dụng để xây dựng một chuỗi để chuyển sang Java sau này và điều đó name()sẽ trả về một bản sao chứ không phải là một tham chiếu. Tôi có thể viết một cái gì đó như thế này:

foo myFoo = getFoo(); // Get the foo from somewhere.
const char* fooCName = foo.name().c_str(); // Woops!  foo.name() creates a temporary that's destructed as soon as this line executes!
jniEnv->NewStringUTF(fooCName);  // No good, fooCName was released when the temporary was deleted.

Nếu người gọi của bạn sẽ thực hiện loại điều này, có thể tốt hơn là sử dụng một số loại con trỏ thông minh hoặc tham chiếu const hoặc ít nhất có một tiêu đề nhận xét cảnh báo khó chịu đối với phương thức foo.name () của bạn. Tôi đề cập đến JNI bởi vì các lập trình viên Java trước đây có thể đặc biệt dễ bị tổn thương với kiểu xâu chuỗi phương thức này có vẻ như vô hại.

28 hữu ích 2 bình luận chia sẻ
17

Một vấn đề cho lợi nhuận tham chiếu const sẽ là nếu người dùng mã hóa một cái gì đó như:

const std::string & str = myObject.getSomeString() ;

Với sự std::stringtrở lại, đối tượng tạm thời sẽ vẫn còn sống và gắn liền với str cho đến khi str vượt quá phạm vi.

Nhưng chuyện gì xảy ra với a const std::string &? Tôi đoán là chúng ta sẽ có một tham chiếu const đến một đối tượng có thể chết khi đối tượng mẹ của nó giải phóng nó:

MyObject * myObject = new MyObject("My String") ;
const std::string & str = myObject->getSomeString() ;
delete myObject ;
// Use str... which references a destroyed object.

Vì vậy, sở thích của tôi là trả về tham chiếu const (bởi vì, dù sao, tôi chỉ dễ dàng gửi được tham chiếu hơn là hy vọng trình biên dịch sẽ tối ưu hóa thêm tạm thời), miễn là hợp đồng sau được tôn trọng: "nếu bạn muốn nó vượt quá Sự tồn tại của đối tượng của tôi, họ sao chép nó trước khi phá hủy đối tượng của tôi "

17 hữu ích 0 bình luận chia sẻ
10

Một số cách triển khai bộ nhớ chia sẻ chuỗi std :: với ngữ nghĩa sao chép khi ghi, do đó, giá trị trả về có thể hiệu quả gần như tham chiếu trả về bạn không phải lo lắng về các vấn đề trọn đời (thời gian chạy nó cho bạn).

Nếu bạn lo lắng về hiệu suất, thì hãy đánh giá nó (<= không thể nhấn mạnh đến mức đó) !!! Hãy thử cả hai cách tiếp cận và đo mức tăng (hoặc thiếu). Nếu một cái tốt hơn và bạn thực sự quan tâm, thì hãy sử dụng nó. Nếu không, sau đó thích giá trị phụ cho sự bảo vệ mà nó cung cấp lại các vấn đề trọn đời được đề cập bởi những người khác.

Bạn biết những gì họ nói về việc đưa ra các giả định ...

10 hữu ích 5 bình luận chia sẻ
7

Được rồi, do đó, sự khác biệt giữa trả lại một bản sao và trả lại tài liệu tham khảo là:

  • Hiệu suất : Trả lại tài liệu tham khảo có thể hoặc không thể nhanh hơn; nó phụ thuộc vào cách std::stringtriển khai trình biên dịch của bạn (như những người khác đã chỉ ra). Nhưng ngay cả khi bạn trả về tham chiếu, việc gán sau lệnh gọi hàm thường liên quan đến một bản sao, như trongstd::string name = obj.name();

  • An toàn : Trả lại tài liệu tham khảo có thể hoặc không gây ra sự cố (tham chiếu lơ lửng). Nếu người dùng chức năng của bạn không biết họ đang làm gì, việc lưu trữ tham chiếu làm tham chiếu và sử dụng nó sau khi đối tượng cung cấp vượt quá phạm vi thì có vấn đề.

Nếu bạn muốn nó nhanh chóng và an toàn, hãy sử dụng boost :: shared_ptr . Đối tượng của bạn có thể lưu trữ bên trong chuỗi như shared_ptrvà trả về a shared_ptr. Theo cách đó, sẽ không có sự sao chép đối tượng đang diễn ra và nó luôn an toàn (trừ khi người dùng của bạn rút con trỏ thô ra get()và làm mọi thứ với nó sau khi đối tượng của bạn đi ra khỏi phạm vi).

7 hữu ích 1 bình luận chia sẻ
4

Tôi sẽ thay đổi nó để trả về const std :: string &. Người gọi có thể sẽ tạo một bản sao kết quả bằng mọi cách nếu bạn không thay đổi tất cả mã gọi, nhưng nó sẽ không gây ra bất kỳ vấn đề nào.

Một nếp nhăn tiềm năng phát sinh nếu bạn có nhiều luồng gọi tên (). Nếu bạn trả về một tham chiếu, nhưng sau đó thay đổi giá trị cơ bản, thì giá trị của trình gọi sẽ thay đổi. Nhưng mã hiện tại không có vẻ an toàn chủ đề nào.

Hãy xem câu trả lời của Dima cho một vấn đề tiềm năng nhưng không thể xảy ra.

4 hữu ích 0 bình luận chia sẻ
3

Có thể hình dung rằng bạn có thể phá vỡ một cái gì đó nếu người gọi thực sự muốn một bản sao, bởi vì họ sắp sửa thay đổi bản gốc và muốn giữ một bản sao của nó. Tuy nhiên, rất có khả năng nó thực sự nên trả lại một tài liệu tham khảo const.

Cách dễ nhất để làm là thử nó và sau đó kiểm tra xem nó có còn hoạt động không, với điều kiện bạn có một số loại thử nghiệm bạn có thể chạy. Nếu không, tôi sẽ tập trung vào viết bài kiểm tra trước, trước khi tiếp tục tái cấu trúc.

3 hữu ích 0 bình luận chia sẻ
2

Có vấn đề gì không? Ngay khi bạn sử dụng trình biên dịch tối ưu hóa hiện đại, các hàm trả về theo giá trị sẽ không liên quan đến một bản sao trừ khi chúng được yêu cầu về mặt ngữ nghĩa.

Xem Câu hỏi thường gặp về C ++ về điều này.

2 hữu ích 1 bình luận chia sẻ
1

Các tỷ lệ khá tốt khi sử dụng điển hình của chức năng đó sẽ không bị hỏng nếu bạn thay đổi thành tham chiếu const.

Nếu tất cả các mã gọi hàm đó nằm dưới sự kiểm soát của bạn, chỉ cần thực hiện thay đổi và xem trình biên dịch có phàn nàn không.

1 hữu ích 0 bình luận chia sẻ
0

Phụ thuộc những gì bạn cần làm. Có thể bạn muốn tất cả người gọi thay đổi giá trị trả về mà không thay đổi lớp. Nếu bạn trả lại tài liệu tham khảo const sẽ không bay.

Tất nhiên, đối số tiếp theo là người gọi sau đó có thể tạo bản sao của riêng họ. Nhưng nếu bạn biết làm thế nào chức năng sẽ được sử dụng và biết điều đó xảy ra dù sao đi nữa, thì có lẽ việc này sẽ giúp bạn tiết kiệm một bước sau đó trong mã.

0 hữu ích 1 bình luận chia sẻ
0

Tôi thường trả lại const & trừ khi tôi không thể. QBziZ đưa ra một ví dụ về trường hợp này. Tất nhiên QBziZ cũng tuyên bố rằng std :: string có ngữ nghĩa sao chép khi viết hiếm khi đúng vì COW liên quan đến rất nhiều chi phí trong môi trường đa luồng. Bằng cách trả lại const & bạn đặt onus cho người gọi để thực hiện đúng với chuỗi trên đầu của họ. Nhưng vì bạn đang xử lý mã đã được sử dụng, có lẽ bạn không nên thay đổi mã trừ khi hồ sơ cho thấy rằng việc sao chép chuỗi này đang gây ra vấn đề hiệu năng lớn. Sau đó, nếu bạn quyết định thay đổi nó, bạn sẽ cần phải kiểm tra kỹ lưỡng để đảm bảo rằng bạn đã không phá vỡ bất cứ điều gì. Hy vọng rằng các nhà phát triển khác mà bạn làm việc không làm những thứ sơ sài như trong câu trả lời của Dima.

0 hữu ích 0 bình luận chia sẻ
0

Trả lại một tài liệu tham khảo cho một thành viên cho thấy việc thực hiện các lớp. Điều đó có thể ngăn cản thay đổi lớp học. Có thể hữu ích cho các phương pháp riêng tư hoặc được bảo vệ trong trường hợp tối ưu hóa là cần thiết. Điều gì nên trả lại C ++

0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c++ const , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading