2010-11-02 36 views
14

Tôi đang lập trình bằng ngôn ngữ C++ ngay bây giờ và tôi thích sử dụng con trỏ. Nhưng có vẻ như các ngôn ngữ khác, mới hơn như Java, C# và Python không cho phép bạn khai báo rõ ràng con trỏ. Nói cách khác, bạn không thể viết cả hai int xint * y và có x là một giá trị trong khi y là một con trỏ, bằng bất kỳ ngôn ngữ nào trong số những ngôn ngữ đó. Lý do đằng sau điều này là gì?Con trỏ có xấu không?

+1

hầu hết mọi người đều nói rằng họ có thể nguy hiểm, vì khả năng gây rối và tạo lỗi seg và rò rỉ bộ nhớ. – Jim

+6

Bạn đang làm gì với con trỏ mà bạn yêu thích? – GManNickG

+0

@ GMan - Tôi nghĩ điều yêu thích của tôi là cho phép một lớp 'A' chứa các con trỏ tới một lớp' B' khác, để nếu một đối tượng 'B' thực sự là một lớp' C' được thừa kế từ 'B', thì 'A' có thể gọi các hàm' C' thay vì các hàm ảo 'B'. Tôi đoán các ngôn ngữ khác có thể được thực hiện khác nhau để tránh sự cần thiết cho con trỏ mặc dù. Ngoài ra, bạn có thể tạo hai lớp bao gồm các con trỏ với nhau để cả hai có thể truy cập các hàm của nhau, nhưng bạn không thể làm điều đó với các giá trị vì nó sẽ tạo ra một vòng lặp vô hạn với lớp 'A' chứa lớp' B' chứa lớp 'A '... – wrongusername

Trả lời

17

Con trỏ không tệ, chúng dễ bị sai. Trong các ngôn ngữ mới hơn, họ đã tìm ra cách để làm những điều tương tự, nhưng ít rủi ro hơn khi tự bắn mình vào chân.

Không có gì sai với con trỏ. Hãy tiếp tục và yêu thương họ.

Hướng tới ví dụ của bạn, tại sao bạn muốn cả x và y trỏ đến cùng một bộ nhớ? Tại sao không phải lúc nào cũng gọi nó là x?

Một điểm nữa, con trỏ có nghĩa là bạn phải tự quản lý tuổi thọ bộ nhớ. Các ngôn ngữ mới hơn thích sử dụng bộ sưu tập rác để quản lý bộ nhớ và cho phép con trỏ sẽ làm cho nhiệm vụ đó trở nên khó khăn.

+0

Vâng, tôi không có nghĩa là 'x' và' y' nên trỏ đến cùng một địa chỉ bộ nhớ mãi mãi, chỉ là bạn không thể khai báo 'x' như lưu trữ một giá trị và' y' như lưu trữ một địa chỉ bộ nhớ. Tôi không biết, tôi thích để luôn có thể lựa chọn rõ ràng giữa việc khai báo một biến với một giá trị hoặc một tham chiếu. – wrongusername

+12

Tất nhiên, không có gì tốt đi kèm miễn phí. Con trỏ C++ mạnh mẽ và nguy hiểm, và trong một số ngôn ngữ khác, trong khi bạn không thể tự bắn mình vào chân, bạn có một thời gian khó khăn. Có những điều cần học hỏi từ các ngôn ngữ bậc cao xử lý các con trỏ có thể được áp dụng cho C++ như thế nào. – ssube

+0

@peachykeen: +1 cho ẩn dụ thông minh. – Cam

6

Con trỏ có thể bị lạm dụng và ngôn ngữ được quản lý thích bảo vệ bạn khỏi những cạm bẫy tiềm ẩn. Tuy nhiên, con trỏ chắc chắn không phải là xấu - chúng là một tính năng tích hợp của ngôn ngữ C và C++, và viết mã C/C++ mà không có chúng đều phức tạp và rườm rà.

12

I'll start with one of my favorite Scott Meyers quotes:

Khi tôi đưa ra đàm phán về xử lý ngoại lệ, tôi dạy cho mọi người hai điều:

  • con trỏ là kẻ thù của bạn, bởi vì họ dẫn đến những loại vấn đề mà auto_ptr được thiết kế loại bỏ.

  • ĐIỂM LÀ BẠN B,, vì thao tác trên con trỏ không thể ném.

Sau đó, tôi nói với họ để có một ngày tốt đẹp :-)


Vấn đề là con trỏ là cực kỳ hữu ích và nó chắc chắn cần thiết để hiểu họ khi lập trình trong C++. Bạn không thể hiểu mô hình bộ nhớ C++ mà không hiểu con trỏ. Khi bạn đang triển khai một lớp sở hữu tài nguyên (ví dụ như một con trỏ thông minh), bạn cần sử dụng con trỏ, và bạn có thể tận dụng sự đảm bảo không ném của chúng để viết các lớp sở hữu tài nguyên ngoại lệ an toàn.

Tuy nhiên, trong mã ứng dụng C++ được viết rõ ràng, bạn không bao giờ phải làm việc với các con trỏ thô. Không bao giờ. Bạn nên luôn sử dụng một số lớp trừu tượng thay vì làm việc trực tiếp với con trỏ:

  • Sử dụng tham chiếu thay cho con trỏ bất cứ khi nào có thể. Các tham chiếu không thể rỗng và chúng làm cho mã dễ hiểu hơn, dễ viết hơn và dễ dàng hơn để viết mã.

  • Sử dụng con trỏ thông minh để quản lý bất kỳ con trỏ nào bạn sử dụng. Các con trỏ thông minh như shared_ptr, auto_ptrunique_ptr giúp đảm bảo rằng bạn không bị rò rỉ tài nguyên hoặc tài nguyên miễn phí sớm.

  • Sử dụng các vùng chứa như các thư mục được tìm thấy trong thư viện chuẩn để lưu trữ bộ sưu tập đối tượng thay vì tự phân bổ mảng. Bằng cách sử dụng các vùng chứa như vectormap, bạn có thể đảm bảo rằng mã của bạn là ngoại lệ an toàn (có nghĩa là ngay cả khi ngoại lệ được ném, bạn sẽ không bị rò rỉ tài nguyên).

  • Sử dụng trình lặp khi làm việc với vùng chứa. Việc sử dụng các trình vòng lặp chính xác hơn rất nhiều so với việc sử dụng con trỏ một cách chính xác và nhiều triển khai thư viện cung cấp hỗ trợ gỡ lỗi để giúp bạn tìm nơi bạn đang sử dụng chúng không chính xác.

  • Khi bạn đang làm việc với API cũ hoặc API của bên thứ ba và bạn hoàn toàn phải sử dụng con trỏ thô, viết một lớp để gói gọn việc sử dụng API đó.

C++ có quản lý tài nguyên tự động dưới dạng Quản lý tài nguyên phạm vi giới hạn (SBRM, còn gọi là Mua tài nguyên là khởi tạo hoặc RAII). Sử dụng nó. Nếu bạn không sử dụng nó, bạn đang làm sai.

+1

Tham chiếu có thể là 'null', ví dụ khi bạn thực hiện tham chiếu đến con trỏ không hợp lệ bị bỏ qua. Xem: http://codepad.org/JEPQpv2v – Paul

+8

@Paul: Hành vi của đoạn mã của bạn không được xác định. "Một tham chiếu null không thể tồn tại trong một chương trình được xác định rõ ràng, bởi vì cách duy nhất để tạo tham chiếu như vậy là liên kết nó với" đối tượng "thu được bằng dereferencing một con trỏ rỗng, gây ra hành vi không xác định" (C++ 03 8.3.2/4). –

+1

@ James McNellis: Truy cập một con trỏ null cũng là hành vi không xác định, nhưng đó là bên cạnh điểm. Vấn đề là, nó hoàn toàn có thể có một tham chiếu 'không hợp lệ' (tức là 'null'/lỗ đen của hành vi không xác định/etc). Nó có thể đạt được một chút, nhưng nó chắc chắn là thú vị để gỡ lỗi khi gặp phải ... – Paul

3

Con trỏ "true" có hai đặc điểm.

  • Nó chứa địa chỉ của một đối tượng (hay nguyên thủy)
    • và để lộ ra bản chất số của địa chỉ đó để bạn có thể làm phép tính.

Điển hình như phép tính số học định nghĩa cho con trỏ là:

  1. Thêm một số nguyên cho một con trỏ vào một mảng, mà trả về địa chỉ của nguyên tố khác.
  2. Trừ hai con trỏ vào cùng một mảng, trả về số phần tử ở giữa (bao gồm một đầu).
  3. So sánh hai con trỏ vào cùng một mảng, cho biết phần tử nào gần với phần đầu của mảng.

Ngôn ngữ được quản lý thường dẫn bạn xuống con đường "tham chiếu" thay vì con trỏ. Tham chiếu cũng giữ địa chỉ của đối tượng khác (hoặc nguyên thủy), nhưng số học không được phép.

Trong số những thứ khác, điều này có nghĩa là bạn không thể sử dụng số học con trỏ để đi ra khỏi phần cuối của mảng và xử lý một số dữ liệu khác bằng cách sử dụng sai loại. Cách khác để tạo thành một con trỏ không hợp lệ được xử lý trong các môi trường như vậy bằng cách sử dụng bộ sưu tập rác.

Cùng với điều này đảm bảo an toàn kiểu, nhưng ở mức độ mất tính tổng quát khủng khiếp.

+0

Tôi không thấy cách bạn mất tổng quát. Không có con trỏ chỉ buộc bạn gọi một spade spade: nếu bạn muốn một mảng các mục, bạn khai báo nó như một mảng các mục và sử dụng các chỉ số nguyên, mà * có thể * được điều khiển bằng số. (Lưu ý rằng trong C và C++, số học con trỏ được chính thức cho phép CHỈ giữa các con trỏ trỏ vào cùng một khối bộ nhớ, nghĩa là mảng). – zvrba

+0

@zvrba: Thực tế là các ngôn ngữ được quản lý không cho phép bạn tạo thành một tham chiếu đến một subobject là một mất mát HUGE của tổng quát.Không thể gõ-pun vào char * là một mất mát HUGE của tổng quát. Ngoài ra, câu trả lời của tôi nhấn mạnh rằng cả hai con trỏ cần phải được vào cùng một khối. Tuy nhiên, con trỏ đến các đối tượng khác nhau có một trật tự không xác định nhưng ** ổn định **, trong môi trường được quản lý bảo đảm này không còn được thực hiện nữa, bộ thu gom rác được phép không chỉ để di chuyển các đối tượng mà còn thay đổi thứ tự của chúng. –

+0

Bạn có ý nghĩa gì khi "mất tính tổng quát"? Và các phôi không an toàn (ví dụ: đối với char *) không phải là thuộc tính vốn có của con trỏ, nó chỉ là một tính năng C. – zvrba

3

Tôi cố gắng trả lời câu hỏi OP của trực tiếp:

Nói cách khác, bạn không thể viết cả int x và int * y, và có x là một giá trị khi y là một con trỏ, trong bất kỳ ngôn ngữ nào trong số . Lý do đằng sau điều này là gì?

Lý do đằng sau đây là mô hình bộ nhớ được quản lý bằng các ngôn ngữ này.Trong C# (hoặc Python, hoặc Java, ...) tuổi thọ của tài nguyên và do đó việc sử dụng bộ nhớ được quản lý tự động bởi thời gian chạy cơ bản, hoặc bởi nó là bộ thu gom rác, chính xác. Tóm lại: Ứng dụng không kiểm soát vị trí của tài nguyên trong bộ nhớ. Nó không được chỉ định - và thậm chí không được bảo đảm duy trì liên tục trong suốt vòng đời của một tài nguyên. Do đó, khái niệm của con trỏ là 'một vị trí của một cái gì đó trong bộ nhớ ảo hoặc vật lý' là hoàn toàn không liên quan.

0

Như ai đó đã đề cập, con trỏ có thể, và thực sự, sẽ đi sai nếu bạn có một ứng dụng lớn. Đây là một trong những lý do mà đôi khi chúng ta thấy Windows gặp sự cố do tạo con trỏ NULL! Cá nhân tôi không thích con trỏ vì nó gây ra rò rỉ bộ nhớ khủng khiếp và không có vấn đề làm thế nào tốt bạn quản lý bộ nhớ của bạn, nó cuối cùng sẽ săn bạn xuống theo một số cách. Tôi đã trải nghiệm điều này rất nhiều với OpenCV khi làm việc xung quanh các ứng dụng xử lý hình ảnh. Có rất nhiều con trỏ trôi nổi xung quanh, đặt chúng vào một danh sách và sau đó lấy chúng sau này gây ra vấn đề cho tôi. Nhưng một lần nữa, có những mặt tốt của việc sử dụng con trỏ và nó thường là một cách tốt để điều chỉnh mã của bạn. Tất cả phụ thuộc vào những gì bạn đang làm, thông số kỹ thuật bạn phải đáp ứng, v.v.

Các vấn đề liên quan