C ++ Iterator, Tại sao không có lớp cơ sở Iterator mà tất cả các trình vòng lặp kế thừa từ


11

Tôi đang học cho một kỳ thi và tôi có một câu hỏi mà tôi đang đấu tranh để đưa ra và trả lời cho.

Tại sao không có lớp cơ sở lặp lặp tồn tại tất cả các trình lặp khác kế thừa từ?

Tôi đoán giáo viên của tôi đang đề cập đến cấu trúc phân cấp từ tham chiếu cpp " http://prntscr.com/mgj542 " và chúng tôi phải cung cấp một lý do khác ngoài lý do tại sao họ nên?

Tôi biết các trình vòng lặp là gì (loại) và chúng được sử dụng để làm việc trên các thùng chứa. Từ những gì tôi hiểu do các cơ sở dữ liệu cơ bản khác nhau có thể, các container khác nhau có các trình lặp khác nhau vì bạn có thể truy cập ngẫu nhiên một mảng, nhưng không phải là một danh sách được liên kết và các container khác nhau yêu cầu các cách di chuyển khác nhau.

Chúng có lẽ là các mẫu chuyên dụng tùy thuộc vào container, phải không?


2
Chia sẻ nghiên cứu của bạn giúp mọi người . Hãy cho chúng tôi những gì bạn đã cố gắng và tại sao nó không đáp ứng nhu cầu của bạn. Điều này chứng tỏ rằng bạn đã dành thời gian để cố gắng tự giúp mình, nó giúp chúng tôi tránh nhắc lại các câu trả lời rõ ràng và hầu hết nó giúp bạn có được câu trả lời cụ thể và phù hợp hơn. Xem thêm Cách hỏi
gnat

5
" Tại sao không có lớp cơ sở lặp không tồn tại tất cả các trình lặp khác thừa hưởng từ? " Ừm ... tại sao phải có một?
Nicol Bolas

Câu trả lời:


14

Bạn đã nhận được câu trả lời chỉ ra lý do tại sao không cần thiết cho tất cả các trình vòng lặp kế thừa từ một lớp cơ sở Iterator duy nhất. Tôi đã có thêm một chút nữa mặc dù. Một trong những mục tiêu của C ++ là trừu tượng hóa với chi phí thời gian chạy bằng không.

Nếu các trình lặp được làm việc bởi tất cả chúng kế thừa từ một lớp cơ sở chung và sử dụng các hàm ảo trong lớp cơ sở để xác định giao diện và các lớp dẫn xuất cung cấp các triển khai của các hàm ảo đó, thì có thể (và thường sẽ) thêm thời gian chạy đáng kể chi phí cho các hoạt động liên quan.

Ví dụ, hãy xem xét một hệ thống phân cấp lặp đơn giản sử dụng các hàm thừa kế và các hàm ảo:

template <class T>
class iterator_base { 
public:
    virtual T &operator*() = 0;
    virtual iterator_base &operator++() = 0;
    virtual bool operator==(iterator_base const &other) { return pos == other.pos; }
    virtual bool operator!=(iterator_base const &other) { return pos != other.pos; }
    iterator_base(T *pos) : pos(pos) {}
protected:
    T *pos;
};

template <class T>
class array_iterator : public iterator_base<T> {
public: 
    virtual T &operator*() override { return *pos; }
    virtual array_iterator &operator++() override { ++pos; return *this; }
    array_iterator(T *pos) : iterator_base(pos) {}
};

Sau đó, hãy cho nó một bài kiểm tra nhanh:

int main() { 
    char input[] = "asdfasdfasdfasdfasdfasdfasdfadsfasdqwerqwerqwerqrwertytyuiyuoiiuoThis is a stringy to search for something";
    using namespace std::chrono;

    auto start1 = high_resolution_clock::now();
    auto pos = std::find(std::begin(input), std::end(input), 'g');
    auto stop1 = high_resolution_clock::now();

    std::cout << *++pos << "\n";

    auto start2 = high_resolution_clock::now();
    auto pos2 = std::find(array_iterator(input), array_iterator(input+sizeof(input)), 'g');
    auto stop2 = high_resolution_clock::now();

    std::cout << *++pos2 << "\n";

    std::cout << "time1: " << duration_cast<nanoseconds>(stop1 - start1).count() << "ns\n";
    std::cout << "time2: " << duration_cast<nanoseconds>(stop2 - start2).count() << "ns\n";
}

[lưu ý: tùy thuộc vào trình biên dịch của bạn, bạn có thể cần phải làm thêm một chút, chẳng hạn như xác định iterator_carget, differ_type, tham chiếu, v.v. để trình biên dịch chấp nhận trình lặp.]

Và đầu ra là:

y
y
time1: 1833ns
time2: 2933ns

[Tất nhiên, nếu bạn chạy mã, kết quả của bạn sẽ không khớp chính xác.]

Vì vậy, ngay cả đối với trường hợp đơn giản này (và chỉ thực hiện khoảng 80 lần tăng và so sánh), chúng tôi đã thêm khoảng 60% chi phí cho một tìm kiếm tuyến tính đơn giản. Đặc biệt là khi các trình vòng lặp ban đầu được thêm vào C ++, khá nhiều người chỉ đơn giản là không chấp nhận một thiết kế có quá nhiều chi phí. Họ có thể sẽ không được tiêu chuẩn hóa, và ngay cả khi họ có, hầu như không ai sẽ sử dụng chúng.


7

Sự khác biệt là giữa một cái gì đó là, và như thế nào một cái gì đó cư xử.

Rất nhiều ngôn ngữ cố gắng gắn kết hai thứ lại với nhau, nhưng chúng là những thứ khá khác biệt.

Nếu như thế nào là gì và thế nào là ...

Nếu mọi thứ kế thừa từ objectđó thì một số lợi ích xảy ra như: bất kỳ biến đối tượng nào cũng có thể giữ bất kỳ giá trị nào. Nhưng đó cũng là sự cọ xát, mọi thứ phải hành xử ( như thế nào ) như một object, và trông giống như ( những gì ) một object.

Nhưng:

  • Điều gì xảy ra nếu đối tượng của bạn không có một định nghĩa có ý nghĩa về sự bình đẳng?
  • Điều gì nếu nó không có một hàm băm có ý nghĩa?
  • Điều gì nếu đối tượng của bạn không thể được nhân bản, nhưng đối tượng có thể được?

Hoặc là objectloại trở nên về cơ bản vô dụng - do đối tượng cung cấp không tương đồng trên tất cả các trường hợp có thể. Hoặc sẽ tồn tại các đối tượng có một định nghĩa gãy / sừng / vô lý của một số tài sản phổ quát được cho là được tìm thấy trên objectđó chứng minh hành vi gần như phổ quát ngoại trừ một số vấn đề.

Nếu những gì không bị ràng buộc với Làm thế nào

Hoặc bạn có thể giữ như thế nào riêng biệt. Sau đó, một số loại khác nhau (không có gì chung ở tất cả những gì ) tất cả có thể hành xử theo cùng một cách như được nhìn thấy từ cộng tác viên như thế nào . Theo nghĩa này, ý tưởng về một Iteratorkhông phải là một cái gì đó cụ thể , mà là làm thế nào . Cụ thể Làm thế nào để bạn tương tác với một thứ khi bạn chưa biết Bạn đang tương tác với cái gì.

Java (và tương tự) cho phép tiếp cận điều này bằng cách sử dụng các giao diện. Một giao diện trong vấn đề này mô tả các phương tiện giao tiếp, và mặc nhiên là một giao thức giao tiếp và hành động được tuân theo. Bất kỳ cái gì tuyên bố chính nó là của một How nhất định , nói rằng nó hỗ trợ giao tiếp và hành động có liên quan được phác thảo bởi giao thức. Điều này cho phép bất kỳ cộng tác viên nào dựa vào Cách thức và không bị sa lầy bằng cách chỉ định chính xác những có thể được sử dụng.

C ++ (và tương tự) cho phép tiếp cận điều này bằng cách gõ vịt. Một mẫu không quan tâm nếu loại cộng tác tuyên bố rằng nó tuân theo một hành vi, chỉ là trong một bối cảnh biên dịch nhất định, đối tượng có thể được tương tác với một cách cụ thể. Điều này cho phép các con trỏ C ++ và các toán tử cụ thể vượt quá các toán tử cụ thể được sử dụng bởi cùng một mã. Bởi vì họ đáp ứng danh sách kiểm tra để được coi là tương đương.

  • hỗ trợ * a, a->, ++ a và ++ -> iterator đầu vào / chuyển tiếp
  • hỗ trợ * a, a->, ++ a, a ++, --a và a-- -> iterator hai chiều

Kiểu cơ bản thậm chí không phải lặp lại một container, nó có thể là bất kỳ cái gì . Ngoài ra, nó cho phép một số cộng tác viên thậm chí còn chung chung hơn, hãy tưởng tượng một hàm chỉ cần a++, một trình lặp có thể đáp ứng điều đó, do đó, một con trỏ cũng vậy, một số nguyên cũng có thể thực hiện bất kỳ đối tượng nào operator++.

Thông số kỹ thuật dưới và trên

Vấn đề với cả hai cách tiếp cận là dưới và trên đặc điểm kỹ thuật.

Sử dụng một giao diện yêu cầu đối tượng khai báo nó hỗ trợ một hành vi nhất định, điều đó cũng có nghĩa là người tạo phải thấm nhuần điều đó ngay từ đầu. Điều này gây ra một số Điều không thể cắt giảm, vì họ đã không tuyên bố điều đó. Nó cũng có nghĩa là bao giờ Cái gì có tổ tiên chung, giao diện đại diện cho How . Điều này không quay trở lại vấn đề ban đầu của object. Điều này khiến các cộng tác viên chỉ định quá mức các yêu cầu của họ, đồng thời khiến một số đối tượng không thể sử dụng được do thiếu khai báo hoặc bị ẩn giấu vì hành vi dự kiến ​​được xác định kém.

Sử dụng một mẫu yêu cầu cộng tác viên làm việc với một cái gì đó hoàn toàn không biết , và thông qua các tương tác của nó, nó xác định một Cách . Ở một mức độ nào đó, điều này làm cho các cộng tác viên viết lách trở nên khó khăn hơn, vì nó phải phân tích Điều gì cho các nguyên tắc giao tiếp của nó (chức năng / trường / v.v.) trong khi tránh các lỗi biên dịch, hoặc ít nhất chỉ ra làm thế nào một cái gì đó không phù hợp với yêu cầu của nó đối với Cách làm . Điều này cho phép cộng tác viên yêu cầu mức tối thiểu tuyệt đối từ bất kỳ Điều gì đã cho, cho phép phạm vi rộng nhất của những gì sẽ được sử dụng. Thật không may, điều này có nhược điểm là cho phép sử dụng vô nghĩa các đối tượng mà kỹ thuật cung cấp các nguyên thủy giao tiếp cho mộtLàm thế nào , nhưng không tuân theo giao thức ngụ ý cho phép tất cả các loại điều xấu xảy ra.

Lặp đi lặp lại

Trong trường hợp này một Iteratorthế nào nó là viết tắt cho một mô tả về tương tác. Bất cứ điều gì phù hợp với mô tả đó là theo định nghĩa một Iterator. Biết cách cho phép chúng tôi viết các thuật toán chung và có một danh sách ngắn về ' Làm thế nào được cung cấp một cái gì đó cụ thể ' cần được cung cấp để làm cho thuật toán hoạt động. Danh sách đó là các hàm / thuộc tính / vv, việc thực hiện chúng có tính đến cụ thể Điều gì đang được xử lý bằng thuật toán.


6

Bởi vì C ++ không cần phải có các lớp cơ sở (trừu tượng) để thực hiện đa hình. Nó có phân nhóm cấu trúc cũng như phân nhóm đề cử .

Khó hiểu trong trường hợp cụ thể của Iterators, các tiêu chuẩn trước đó được định nghĩa std::iteratorlà (khoảng)

template <class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, class Reference = T&>
struct iterator {
    using iterator_category = Category;
    using value_type = T;
    using difference_type = Distance;
    using pointer = Pointer;
    using reference = Reference;
}

Tức là chỉ là một nhà cung cấp của các loại thành viên cần thiết. Nó không có bất kỳ hành vi thời gian chạy nào và không được dùng trong C ++ 17

Lưu ý rằng ngay cả điều này không thể là một cơ sở chung, vì một mẫu lớp không phải là một lớp, mỗi lần khởi tạo đứng một mình so với các lớp khác.



5

Một lý do là các trình vòng lặp không phải là các thể hiện của một lớp. Con trỏ là các trình vòng lặp hoàn toàn tốt trong nhiều trường hợp, ví dụ, và vì đó là những người nguyên thủy nên họ không thể thừa hưởng từ bất cứ điều gì.

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.