Khi nào sử dụng typedef?


14

Tôi hơi bối rối về việc khi nào và khi nào tôi nên sử dụng typedef trong C ++. Tôi cảm thấy đó là một hành động cân bằng giữa dễ đọc và rõ ràng.

Đây là một mẫu mã mà không có bất kỳ typedefs nào:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    static std::map<std::tuple<std::vector<int>::const_iterator,
                               std::vector<int>::const_iterator>,
                    int> lookup_table;

    std::map<std::tuple<std::vector<int>::const_iterator,
                        std::vector<int>::const_iterator>, int>::iterator lookup_it =
        lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

IMO khá xấu xí. Vì vậy, tôi sẽ thêm một số typedefs trong hàm để làm cho nó trông đẹp hơn:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    typedef std::tuple<std::vector<int>::const_iterator,
                       std::vector<int>::const_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Mã vẫn còn một chút vụng về, nhưng tôi thoát khỏi hầu hết các tài liệu ác mộng. Nhưng vẫn còn các trình lặp vector int, biến thể này loại bỏ những thứ đó:

typedef std::vector<int>::const_iterator Input_iterator;

int sum(Input_iterator first, Input_iterator last)
{
    typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Điều này có vẻ sạch sẽ, nhưng nó vẫn có thể đọc được?

Khi nào tôi nên sử dụng typedef? Ngay khi tôi có một loại ác mộng? Ngay khi nó xảy ra nhiều hơn một lần? Tôi nên đặt chúng ở đâu? Tôi nên sử dụng chúng trong chữ ký chức năng hoặc giữ chúng để thực hiện?


1
Không trùng lặp, nhưng phần nào liên quan đến các lập trình viên
tôi.stackexchange.com/questions/130679/iêu

typedef Input_iterator std::vector<int>::const_iterator;là ngược
Per Johansson

1
Có sự khác biệt giữa dễ đọc và rõ ràng?
Neil

Khi #definekhông đủ tốt.
Thomas Eding

Câu trả lời:


6

Ví dụ cuối cùng của bạn rất dễ đọc, nhưng nó phụ thuộc vào nơi bạn xác định typedef. Typedefs phạm vi cục bộ (như trong ví dụ thứ hai của bạn) là IMVHO hầu như luôn luôn là một chiến thắng.

Tôi vẫn thích ví dụ thứ ba của bạn nhất, nhưng bạn có thể muốn nghĩ về việc đặt tên và đưa ra các tên lặp cho biết ý định của container.

Một tùy chọn khác là tạo một mẫu ra khỏi chức năng của bạn, để nó cũng hoạt động với các thùng chứa khác nhau. Dọc theo những đường kẻ

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

đó cũng là rất nhiều trong tinh thần của STL.


2

Hãy nghĩ về một typedefkhai báo biến tương đương với một hàm: nó ở đó để bạn ...

  • ... không phải lặp lại chính mình khi sử dụng lại cùng loại (mà hai ví dụ đầu tiên của bạn làm).
  • ... Có thể che giấu các chi tiết chính của loại để chúng không luôn được hiển thị.
  • ... Đảm bảo rằng các thay đổi về loại được phản ánh ở mọi nơi chúng được sử dụng.

Cá nhân, tôi trừng mắt nếu tôi phải đọc tên loại dài như std::vector<int>::const_iteratorlặp đi lặp lại.

Ví dụ thứ ba của bạn không lặp lại một cách không cần thiết và dễ đọc nhất.


1

typedefkhai báo về cơ bản giống như mục đích đóng gói. Vì lý do đó, chúng hầu như luôn phù hợp nhất trong tệp tiêu đề, tuân theo các quy ước đặt tên giống như các lớp của bạn, bởi vì:

  • Nếu bạn cần typedef , rất có thể những người gọi cũng sẽ như vậy, đặc biệt là trong ví dụ của bạn, nơi nó được sử dụng trong các đối số.
  • Nếu bạn cần thay đổi loại vì bất kỳ lý do gì, bao gồm thay thế nó bằng lớp của riêng bạn, bạn sẽ chỉ phải làm điều đó ở một nơi.
  • Nó làm cho bạn ít mắc lỗi do viết nhiều loại phức tạp.
  • Nó ẩn các chi tiết thực hiện không cần thiết.

Bên cạnh đó, mã ghi nhớ của bạn sẽ sạch hơn rất nhiều nếu bạn trừu tượng hóa nó hơn nữa, như:

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);

Đề xuất của bạn có thể trông sạch hơn, nhưng nó lãng phí thời gian bằng cách thực hiện tra cứu hai lần.
Derek Ledbetter

Đúng, đó là một sự đánh đổi có chủ ý. Tuy nhiên, có nhiều cách để làm điều đó với một tra cứu gần như sạch sẽ, đặc biệt là nếu bạn không lo lắng về an toàn của luồng.
Karl Bielefeldt
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.