it-swarm-vi.com

Tại sao con trỏ không được khuyến nghị khi mã hóa bằng C ++?

Tôi đọc từ đâu đó rằng khi sử dụng C++ thì không nên sử dụng con trỏ. Tại sao con trỏ lại là một ý tưởng tồi khi bạn đang sử dụng C++. Đối với các lập trình viên C đã quen sử dụng các con trỏ, cách thay thế và cách tiếp cận tốt hơn trong C++ là gì?

45
Joshua Partogi

Tôi nghĩ rằng họ có nghĩa là bạn nên sử dụng con trỏ thông minh thay vì con trỏ thông thường.

Trong khoa học máy tính, một con trỏ thông minh là một kiểu dữ liệu trừu tượng mô phỏng một con trỏ trong khi cung cấp các tính năng bổ sung, chẳng hạn như thu thập rác tự động hoặc kiểm tra giới hạn. Các tính năng bổ sung này nhằm giảm các lỗi gây ra bởi việc sử dụng sai con trỏ trong khi vẫn giữ được hiệu quả. Con trỏ thông minh thường theo dõi các đối tượng mà chúng trỏ đến nhằm mục đích quản lý bộ nhớ.

Việc sử dụng sai các con trỏ là một nguồn chính của các lỗi: việc phân bổ, phân bổ và tham chiếu liên tục phải được thực hiện bởi một chương trình được viết bằng cách sử dụng các con trỏ dẫn đến nguy cơ rò rỉ bộ nhớ sẽ xảy ra. Con trỏ thông minh cố gắng ngăn chặn rò rỉ bộ nhớ bằng cách tự động phân bổ tài nguyên: khi con trỏ (hoặc cuối cùng trong một loạt các con trỏ) đến một đối tượng bị phá hủy, ví dụ vì nó nằm ngoài phạm vi, đối tượng nhọn cũng bị phá hủy.

Trong C++, trọng tâm sẽ là thu gom rác và ngăn rò rỉ bộ nhớ (chỉ để đặt tên hai). Con trỏ là một phần cơ bản của ngôn ngữ, vì vậy không sử dụng chúng là điều không thể, ngoại trừ trong các chương trình trival nhất.

58
jmq

Vì tôi là người đã xuất bản cuộc bút chiến từ don don sử dụng f * cking con trỏ Tôi cảm thấy rằng tôi nên bình luận ở đây.

Trước hết, như một cuộc bút chiến, rõ ràng nó đại diện cho một quan điểm cực đoan. Có are sử dụng con trỏ (thô) hợp pháp. Nhưng tôi (và nhiều lập trình viên C++ chuyên nghiệp) cho rằng những trường hợp này cực kỳ hiếm. Nhưng những gì chúng tôi thực sự có nghĩa là sau đây:

Đầu tiên:

Con trỏ thô phải trong mọi trường hợp không có bộ nhớ riêng.

Ở đây, bộ nhớ riêng của Nhật Bản về cơ bản có nghĩa là tại một thời điểm nào đó delete được gọi trên con trỏ đó (nhưng nó nói chung chung hơn thế). Câu lệnh này có thể được coi là tuyệt đối. only ngoại lệ là khi thực hiện con trỏ thông minh của riêng bạn (hoặc khác chiến lược quản lý bộ nhớ). Và thậm chí ở đó bạn nên bình thường still sử dụng một con trỏ thông minh ở mức thấp.

Lý do cho việc này khá đơn giản: con trỏ thô mà bộ nhớ riêng giới thiệu một nguồn lỗi. Và những lỗi này rất phổ biến trong phần mềm hiện có: rò rỉ bộ nhớ và xóa hai lần - cả hai đều là hậu quả trực tiếp của quyền sở hữu tài nguyên không rõ ràng (nhưng đi theo hướng ngược lại).

Vấn đề này có thể được loại bỏ hoàn toàn, hầu như không mất phí, chỉ bằng cách sử dụng con trỏ thông minh thay vì con trỏ thô (cảnh báo: điều này vẫn đòi hỏi phải suy nghĩ, tất nhiên; con trỏ chia sẻ có thể dẫn đến chu kỳ và do đó một lần một lần nữa để rò rỉ bộ nhớ - nhưng điều này là dễ dàng tránh được).

Thứ hai:

Hầu hết việc sử dụng con trỏ trong C++ là không cần thiết.

Không giống như các ngôn ngữ khác, C++ có sự hỗ trợ rất mạnh mẽ về ngữ nghĩa giá trị và đơn giản là không cần sự chỉ định của con trỏ. Điều này ngay lập tức nhận ra - về mặt lịch sử, C++ được phát minh để tạo điều kiện cho việc định hướng đối tượng dễ dàng trong C và phụ thuộc rất nhiều vào việc xây dựng các biểu đồ đối tượng được kết nối bằng con trỏ. Nhưng trong C++ hiện đại, mô hình này hiếm khi là sự lựa chọn tốt nhất và các thành ngữ C++ hiện đại thường không cần con trỏ . Chúng hoạt động trên giá trị chứ không phải con trỏ.

Thật không may, thông báo này vẫn chưa được bắt gặp trong phần lớn cộng đồng người dùng C++. Kết quả là, hầu hết các mã C++ được viết vẫn bị lấp đầy bởi các con trỏ thừa, làm cho mã phức tạp, chậm và bị lỗi/không đáng tin cậy.

Đối với ai đó biết C++ hiện đại, điều đó rõ ràng rằng bạn rất hiếm khi cần any con trỏ (thông minh hoặc thô; trừ khi sử dụng chúng làm công cụ lặp). Mã kết quả là ngắn hơn, ít phức tạp hơn, dễ đọc hơn, thường hiệu quả hơn và đáng tin cậy hơn.

97
Konrad Rudolph

Đơn giản là vì có những tóm tắt có sẵn cho bạn, trong đó ẩn các khía cạnh bình thường hơn của việc sử dụng các con trỏ, chẳng hạn như truy cập vào bộ nhớ thô và dọn dẹp sau khi phân bổ của bạn. Với các con trỏ thông minh, các lớp container và các mẫu thiết kế như RAII, nhu cầu sử dụng các con trỏ thô bị giảm đi. Điều đó nói rằng, giống như bất kỳ sự trừu tượng nào, bạn nên hiểu cách chúng thực sự hoạt động trước khi vượt ra ngoài chúng.

15
Ed S.

Tương đối đơn giản, tâm lý C là "Có vấn đề? Sử dụng một con trỏ". Bạn có thể thấy điều này trong chuỗi C, con trỏ hàm, con trỏ-as-iterators, con trỏ tới con trỏ, con trỏ void - ngay cả trong những ngày đầu của C++ với con trỏ thành viên.

Nhưng trong C++, bạn có thể sử dụng các giá trị cho nhiều hoặc tất cả các tác vụ này. Cần một sự trừu tượng hóa chức năng? std::function. Đó là một giá trị đó là một chức năng. std::string? Đó là một giá trị, đó là một chuỗi. Bạn có thể thấy các cách tiếp cận tương tự trên C++. Điều này làm cho việc phân tích mã dễ dàng hơn nhiều cho cả người và trình biên dịch.

11
DeadMG

Một trong những lý do là ứng dụng con trỏ quá rộng. Chúng có thể được sử dụng để lặp qua các container, để tránh sao chép các đối tượng lớn khi chuyển sang chức năng, quản lý thời gian sống không tầm thường, truy cập vào các vị trí ngẫu nhiên trong bộ nhớ, v.v. Và một khi bạn sử dụng chúng cho một mục đích, các tính năng khác của chúng sẽ khả dụng ngay lập tức độc lập về ý định.

Lựa chọn một công cụ cho mục đích chính xác làm cho mã đơn giản hơn và mục đích rõ ràng hơn - các trình lặp cho các lần lặp, các con trỏ thông minh để quản lý thời gian sống, v.v.

10
maxim1000

Bên cạnh những lý do đã được liệt kê, có một lý do rõ ràng: tối ưu hóa tốt hơn. Phân tích răng cưa quá phức tạp trong việc sử dụng một số liệu của con trỏ, trong khi các tham chiếu gợi ý một trình tối ưu hóa, do đó có thể phân tích răng cưa sâu hơn nhiều nếu chỉ sử dụng các tham chiếu.

3
SK-logic

Bên cạnh nguy cơ rò rỉ bộ nhớ được nêu bởi con số @jmquigley và số học con trỏ có thể được coi là có vấn đề vì con trỏ có thể trỏ mọi nơi trong bộ nhớ gây ra "khó tìm lỗi" và "lỗ hổng bảo mật".

Đó là lý do tại sao chúng gần như bị bỏ rơi trong C # và Java.

2
k3b