Có một cách tiêu chuẩn để chỉ ra rằng một hàm trả về một con trỏ mới?


8

Đôi khi tôi muốn ủy thác việc xây dựng các đối tượng mà một lớp sở hữu cho một chức năng riêng biệt. Cái gì đó như

Vertex* new_vertex(const Options& options) {
  // do stuff...
  return new Vertex(...);
}

trong đó hàm chỉ dành cho mục đích sử dụng trong một lớp sở hữu Vertex. Rõ ràng chức năng này có thể gây ra một số nhầm lẫn rò rỉ bộ nhớ, vì vậy tôi muốn làm cho nó rõ ràng nhất có thể. Có một quy ước đặt tên cho các chức năng như vậy?


11
Đúng. // TODO: Fix allocation of raw pointer.
Gort Robot

3
@StevenBurnap Tôi sẽ không sử dụng từ "sửa chữa" trừ khi điều đó không hoạt động.
dùng253751

1
Chỉ là một nhận xét về mối quan tâm của "nhầm lẫn". Điều quan trọng không kém là người gọi chăm sóc đúng cách đối tượng trả lại. Con trỏ thông minh có thể làm cho việc này trở nên dễ dàng (bằng cách biến quản lý bộ nhớ thành thói quen cơ học thay vì nỗ lực tinh thần liên tục), nhưng nếu người gọi không có bất kỳ chính sách rõ ràng hoặc tiêu chuẩn mã hóa hoặc khái niệm "quyền sở hữu đối tượng" phân cấp, cuối cùng nó sẽ vẫn gặp rắc rối. Trong một số trường hợp, các lập trình viên cơ sở thậm chí có thể cố gắng phá vỡ chức năng unique_ptrbằng cách gọi release()hàm của nó và sử dụng các con trỏ thô như các cách cũ.
rwong

1
@StevenBurnap Tại sao không đơn giản // FIXME: Allocation of raw pointer?
Tobias Kienzler

1
Chức năng của bạn làm cho nó khá rõ ràng với tôi. Kiểu trả về không phải theo giá trị hoặc tham chiếu - một trong số đó sẽ làm cho việc cố gắng xóa bộ nhớ thành một cú pháp cú pháp không theo bản năng. Hàm được gọi new_vertexđể tôi biết đối tượng mới được đúc. Bạn có thể gọi nó Create_new_vertexđể được rõ ràng hơn. Đối với ý tưởng rằng bạn không nên quản lý bộ nhớ heap mà không có con trỏ thông minh, không bao giờ nhìn thấy sự thật trong đó - thực tế nếu bạn không thể quản lý bộ nhớ heap mà không có chúng, bạn cũng không có doanh nghiệp quản lý bộ nhớ heap với chúng!
Grimm The Opiner

Câu trả lời:


21

Trả lại một unique_ptr:

std::unique_ptr<Vertex> new_vertex(const Options& options) {
  // do stuff...
  return std::make_unique<Vertex>(...);
}

Chỉ có thể có một người unique_ptrchỉ vào một đối tượng nhất định (trừ khi bạn lạm dụng nó bằng cách truyền tới Vertex*và quay lại, dù sao đi nữa). Bạn không bao giờ có thể sao chép unique_ptr, chỉ di chuyển nó. Khi a unique_ptrbị phá hủy (và chưa được di chuyển ra khỏi), nó thậm chí còn deletelà đối tượng cho bạn.

make_uniquetạo một cái mới Vertexvà kết thúc nó trong một unique_ptr; các đối số bạn vượt qua để make_uniquelà những lý lẽ nó đi đến các nhà xây dựng.


4
Điều này rất hữu ích, nhưng nó không thực sự trả lời câu hỏi của tôi. Tôi biết về con trỏ thông minh, tôi chỉ muốn biết liệu có một quy ước đặt tên cho các hàm trả về con trỏ thô mà người dùng phải quan tâm không. Tôi đoán câu trả lời là "không, không, bởi vì bạn không bao giờ nên làm điều đó"?
Shep

3
@Shep Nếu bạn muốn chỉ cho người gọi hàm của bạn rằng nó tạo ra một đối tượng mới, unique_ptrthì thực hiện điều đó. Nếu nó phải là một cái tên vì một lý do nào đó, thì tôi đoán nó không phải, nhưng tại sao nó phải là một cái tên?
dùng253751

8
@Shep Tên là những thứ vô hình và mọi người có thể tự do bỏ qua chúng hoàn toàn. Các loại mặt khác được thực thi bởi trình biên dịch, có nghĩa là nó đòi hỏi nỗ lực đáng kể để phá vỡ công cụ. Không bao giờ gửi tên để thực hiện công việc của một loại.
ComicSansMS

1
@Shep không có hại khi trả về một con trỏ thô trong trường hợp này . Trường hợp này là bạn đã tạo một đối tượng tùy thuộc vào người gọi để quản lý thời gian tồn tại. Người gọi luôn có thể thả thẳng vào một con trỏ thông minh nếu họ nghĩ họ cần.
Grimm The Opiner

1
@immibis: Tại sao bạn ghi đè lên khấu trừ mẫu-đối số std::make_unique? Điều đó không cần thiết dài dòng và dễ bị lỗi ...
Ded repeatator

2

Có một cách tiêu chuẩn để chỉ ra rằng một hàm trả về một con trỏ mới?

Không, không có "cách tiêu chuẩn" (nhưng có một chính sách thiết kế API hiện được coi là "cách thực hành tốt nhất").

Do sự không rõ ràng này ("một hàm trả về con trỏ muốn tôi làm gì với nó?"), Hiện tại nó được coi là cách thực hành tốt nhất để áp dụng chính sách trọn đời và quyền sở hữu, thông qua loại trả về:

<vertex pointer type> new_vertex(const Options& options);

<vertex pointer type>có thể là std::unique_ptr("new_vertex không sở hữu con trỏ") hoặc std::shared_ptr("mã máy khách không sở hữu con trỏ"), hoặc một cái gì khác, đã xác định rõ ngữ nghĩa quyền sở hữu (ví dụ: Vertex const * constsẽ chỉ ra mã máy khách "đọc địa chỉ và giá trị, nhưng không thay đổi / không xóa con trỏ ").

Nói chung, bạn không nên trả về một con trỏ thô (nhưng trong một số trường hợp, "tính thực tế đánh bại độ tinh khiết").

TLDR : có một cách thực hành tốt nhất ( ), nhưng không phải là một cách tiêu chuẩn (bằng ngôn ngữ).

Chỉnh sửa :

trong đó chức năng chỉ được sử dụng trong một lớp sở hữu Vertex

Nếu lớp sở hữu Vertex, tôi sẽ viết nó như thế này:

class SomeClass // owns the vertex
{
    void do_stuff() // uses the vertex internally
    {
        init_vertex(); // see below
        assert(vertex.get());
        // use vertex as needed
    }
private:
    // "class owns the Vertex"
    std::unique_ptr<Vertex> vertex;

    // sets the internal vertex
    // function doesn't return a pointer of any kind
    void init_vertex(const Options& options); // or "reset_", "refresh_", 
                                              // or "make_" vertex,
                                              // if the pointer can change
                                              // throughout the lifetime
                                              // of a SomeClass instance
};

0

Vâng, có hàng tá "tiêu chuẩn" về điều này

XKCD

Câu trả lời khuyến nghị unique_ptrcó lẽ là câu trả lời tốt nhất, nhưng nếu bạn đang xem các con trỏ thô, mọi người đều phát triển tiêu chuẩn của riêng họ.

Nói chung chức năng đặt tên create_____hay new______hoặc alloc_____có xu hướng để ngụ ý một đối tượng mới.

Tuy nhiên

Hãy xem xét rằng bạn đang trộn các hành vi. Bạn không thực sự quan tâm liệu đây có phải là một thể hiện mới của một đối tượng hay không. Điều bạn quan tâm là liệu người gọi có chịu trách nhiệm tiêu diệt đối tượng hay không. Có nhiều API giao trách nhiệm cho một đối tượng hiện có và các hàm đó sẽ cần một sơ đồ đặt tên phù hợp. Có lẽ give______.

Nếu bạn sẵn sàng rời xa C ++ một chút ...
Nếu bạn sẵn sàng coi Objective-C đủ giống với C ++ để trở thành nguồn cho câu trả lời, thực sự có một câu trả lời thực sự trong ngôn ngữ đó. Objective-C quản lý tuổi thọ của các đối tượng bằng cách sử dụng số tham chiếu. Họ cần một cách để mô tả liệu bạn có được giao trách nhiệm cho một đối tượng hay chỉ đơn thuần là một tham chiếu đến một đối tượng hiện có. Nếu bạn nhận được câu trả lời sai, bạn sẽ nhận được số lần đếm sai và mất đồ vật. Theo đó, họ đưa ra một quy tắc: mọi đối tượng bạn được trả lại đều thuộc sở hữu của người khác (vì vậy bạn phải tự tăng và giảm số lượng ref), ngoại trừ các lệnh gọi tạo con trỏ đã tăng cho bạn (bạn chỉ phải giảm). Các cuộc gọi này được xác định bởi tên phương thức. Các phương thức bắt đầu bằng alloc new copyhoặcmutableCopyluôn trả lại một đối tượng mới. Vì vậy, tiêu chuẩn đã được thực thi bởi ngôn ngữ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.