Việc tạo một phương thức tĩnh lưu bộ nhớ trên một lớp bạn sẽ có nhiều trường hợp phải không?


8

Đáp lại câu trả lời của Aaronaught cho câu hỏi tại:

Tôi không thể sử dụng tất cả các phương thức tĩnh?

Không có ít bộ nhớ được sử dụng cho một phương thức tĩnh? Tôi có ấn tượng rằng mỗi phiên bản đối tượng mang theo phiên bản thực thi riêng của hàm chức năng không tĩnh.

Bất kể có bao nhiêu chi phí liên quan đến việc gọi một phương thức tĩnh, bất kể thiết kế OO kém và đau đầu có thể xảy ra, không phải nó sử dụng ít bộ nhớ hơn khi chạy?

Đây là một ví dụ:

Tôi tạo một vectơ của các đối tượng khởi tạo bằng không. Mỗi đối tượng chứa một phần dữ liệu (một hình tam giác gồm chín đôi). Mỗi đối tượng được điền theo thứ tự từ dữ liệu đọc từ tệp .stl. Chỉ có một phương pháp tĩnh là cần thiết. Thiết kế OO thích hợp chỉ ra rằng một phương thức xử lý dữ liệu trực tiếp nên được phân phối cho từng đối tượng. Đây là giải pháp OO tiêu chuẩn:

foreach(obj in vec) {
  obj.readFromFile(fileName);
}

Mỗi obj mang readFromFilemã được biên dịch cùng với dữ liệu!

Bộ nhớ là mối quan tâm nhiều hơn hiệu năng trong trường hợp này và có RẤT NHIỀU dữ liệu trên một hệ thống bị hạn chế.

Các giải pháp:

  1. Phương thức không gian tên (tuyệt vời cho C ++ nhưng không khả dụng trong Java)
  2. Một phương thức tĩnh trong lớp obj. Mã thực thi được giữ ở một nơi trong thời gian chạy. Có một chi phí nhỏ để gọi phương thức.
  3. Một lớp cha mà từ đó obj được dẫn xuất, chứa phương thức riêng readFromFile. Gọi với super.callPrivateMethod()cuộc gọi nào readFromFile. Lộn xộn, và vẫn còn một số bộ nhớ trên mỗi đối tượng.
  4. Triển khai readFromFilebên ngoài phạm vi của obj, vì vậy trong lớp của vec hoặc trong lớp gọi. Điều này, theo tôi, phá vỡ đóng gói dữ liệu.

Tôi nhận ra đối với một lượng lớn dữ liệu, một đối tượng rõ ràng cho mỗi tam giác không phải là cách tiếp cận tốt nhất. Đây chỉ là một ví dụ.


7
Trong ngôn ngữ nào bạn nghĩ rằng mã được sao chép cho mỗi trường hợp? Điều này không xảy ra trong Java, C #, C ++.
joshp

2
Vâng, nó có thể xảy ra với một số ngôn ngữ động ở thời kỳ đầu, hoặc là một tác dụng phụ của sự phản ánh bị lạm dụng. Nhưng nói chung, không, một Byte sẽ không lớn hơn nhiều so với byte mà nó chứa.
Matthew Mark Miller

1
Ngôn ngữ duy nhất tôi biết về nơi bạn có thể có hành vi gây lãng phí bộ nhớ này là Javascript, nhưng ngay cả khi đó bạn cũng phải hoàn toàn thất bại tại OO nguyên mẫu để thực hiện. Những gì bạn phải làm trong JS là đặt các phương thức (cả tĩnh và không tĩnh) trên đối tượng nguyên mẫu, để tất cả các đối tượng kế thừa từ nguyên mẫu đó sẽ chia sẻ một hàm đó mà không bị trùng lặp.
Ixrec

2
Bạn đã từng cân nhắc viết một chương trình nhỏ tạo ra hàng ngàn đối tượng như vậy và tự mình thực hiện các phép đo chưa?
Bryan Oakley

1
Không phải là câu trả lời cho mọi câu hỏi liên quan đến OO: Dependency Injection? Nếu bạn bận tâm về chi phí bộ nhớ, bạn có thể thiết kế một đối tượng "tiết kiệm" mà bạn tạo một thể hiện, có thể được tiêm ....
Pieter B

Câu trả lời:


19

Các phương thức không được lưu trữ trên cơ sở từng trường hợp, ngay cả với các phương thức ảo. Chúng được lưu trữ trong một vị trí bộ nhớ duy nhất và chúng chỉ "biết" chúng thuộc về đối tượng nào vì thiscon trỏ được truyền khi bạn gọi chúng.

Bộ nhớ bổ sung duy nhất cần có trong C ++ là nếu bạn đang sử dụng các phương thức ảo, yêu cầu một con trỏ phụ duy nhất cho mỗi cá thể để trỏ vào bảng phương thức ảo (tất nhiên trong Java bạn luôn có một lớp cơ sở với các phương thức ảo Object, vì vậy không thể tránh khỏi ).

Nếu nó hoạt động theo cách bạn mô tả, các ngôn ngữ OO sẽ không phổ biến lắm! Hãy thêm nhiều phương thức bạn cần vào đối tượng của bạn. Nó sẽ không ảnh hưởng đến việc sử dụng bộ nhớ của họ.


Ngoài ra còn có một chi phí cho mỗi phương thức ảo cho mỗi lớp. Và Java là ảo theo mặc định ...
Ded repeatator

1
@Ded repeatator: Điều đó đúng, nhưng bạn cũng có thể lập luận rằng có một chi phí chung để có chức năng / phương thức ở vị trí đầu tiên. Mỗi chức năng chiếm bộ nhớ và một chức năng ảo cần thêm một chút nữa (khoảng 4 byte).
Bart van Ingen Schenau

@BartvanIngenSchenau Hoặc 8 byte và cho mỗi lớp dẫn xuất một lần nữa. Mặc dù cả hai ước tính đều đánh giá rất thấp chi phí trong Java ...
Ded repeatator

3
@Ded repeatator Tuy nhiên, chi phí đó là cho mỗi lớp (và mỗi lớp dẫn xuất), không phải cho mỗi trường hợp.
Craig

7

Bất kể có bao nhiêu chi phí liên quan đến việc gọi một phương thức tĩnh, bất kể thiết kế OO kém và đau đầu có thể xảy ra, không phải nó sử dụng ít bộ nhớ hơn khi chạy?

Điều này khá phức tạp, nhưng tôi sẽ nói "chủ yếu là không".


2

Một phương thức nên là "tĩnh" (nhiều ngôn ngữ sử dụng thuật ngữ "phương thức lớp" tốt hơn nhiều) nếu nó không đề cập đến một thể hiện của lớp. Nó phải là một phương thức cá thể nếu nó đề cập đến một thể hiện của lớp.

Đưa ra quyết định thiết kế dựa trên khả năng mơ hồ của một số tiết kiệm bộ nhớ thay vì sử dụng đúng phương thức lớp và phương thức cá thể là một ý tưởng rất, rất, rất xấu.


2
Lời khuyên tốt, nhưng không thực sự trả lời câu hỏi.
JacquesB

1
Với giả định mạnh mẽ rằng đây là một câu hỏi XY, nó thực hiện.
gnasher729

1

Về nguyên tắc, chỉ có một bản sao của mã, cho dù đó là chức năng thành viên tĩnh hay không:

  • Trong C ++, bạn có thể dễ dàng xác minh điều này bằng cách xem danh sách trình biên dịch mã được tạo.
  • Tôi không phải là chuyên gia Java, nhưng từ đặc tả JNI, bạn có thể suy luận đó là logic tương tự: bạn có thể tìm nạp một tham chiếu đến một hàm và gọi nó nhiều lần cho các đối tượng khác nhau (chuyển nó thành tham chiếu lớp làm đối số, cũng như tham chiếu đối tượng nếu nó không phải là một phương thức tĩnh).

Do đó, sử dụng hàm tĩnh hoặc hàm không tĩnh sẽ không thay đổi dấu chân bộ nhớ.

Trên thực tế, có một sự khác biệt rất nhỏ, cả về mã được tạo cũng như trong bộ nhớ được sử dụng trên ngăn xếp trong thời gian thực hiện của hàm:

  • Một cuộc gọi đến hàm không tĩnh yêu cầu chuyển một tham chiếu / con trỏ tới đối tượng mà hàm / phương thức được áp dụng. Điều này thường đòi hỏi một cú đẩy bổ sung và một lệnh pop trong chuỗi cuộc gọi.
  • Một cuộc gọi đến một chức năng tĩnh không yêu cầu chi phí này.

Nhưng điều này thực sự không đáng kể: chúng ta đang nói về một hoặc hai hướng dẫn máy nhiều hơn hoặc ít hơn so với mã đầy đủ. Vì vậy, nó chắc chắn không phải là một cái gì đó để lo lắng.

Chênh lệch tiêu thụ ngăn xếp tại thời gian chạy được giới hạn trong thời gian thực hiện hàm, với kích thước của một con trỏ. Điều này cũng không đáng kể, trừ khi bạn đang nghĩ về hàm aa được gọi đệ quy một số lần rất lớn (vài triệu lần).

Vì vậy, để kết luận, tôi hoàn toàn chia sẻ ý kiến ​​của gnasher729 : tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Nếu chức năng độc lập với đối tượng làm cho nó tĩnh và đó phải là tiêu chí duy nhất.


Tôi tin rằng trình biên dịch C ++ có thể tránh chuyển tham chiếu đến this, nếu nó không được sử dụng trong hàm. Trong Java có lẽ JIT có thể làm điều này.
Oliv

0

Thật dễ dàng để tập trung vào những thứ như tối ưu hóa việc sử dụng bộ nhớ hoặc giảm dòng mã nhưng khi bạn bắt đầu phải duy trì các chương trình mà mọi người sử dụng / phá vỡ và các lập trình viên khác thêm chức năng vào ... và có thể giới thiệu các lỗi ... không phải là chương trình gốc của bạn có thể có các lỗi khiến nó vượt qua thử nghiệm, bạn sẽ thấy rằng việc tập trung vào khả năng đọc / khả năng duy trì của mã có thể quan trọng hơn. Tôi có thể tạo một mảng bit tốc độ cao để theo dõi một loạt các điểm dữ liệu nhưng trừ khi tôi bọc mảng bit đó trong một đối tượng với các trình truy cập dễ hiểu, lập trình viên tiếp theo chạm vào mã đó có thể sẽ không sử dụng nó, thay thế nó hoặc thực hiện một số phù thủy gớm ghiếc .

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.