Khi nào KHÔNG sử dụng hàm hủy ảo?


48

Tôi tin rằng tôi đã tìm kiếm nhiều lần về các công cụ hủy diệt ảo, hầu hết đề cập đến mục đích của các công cụ hủy diệt ảo và lý do tại sao bạn cần các công cụ hủy diệt ảo. Ngoài ra tôi nghĩ rằng trong hầu hết các trường hợp, các hàm hủy cần phải là ảo.

Sau đó, câu hỏi là: Tại sao c ++ không đặt tất cả các hàm hủy theo mặc định? hoặc trong các câu hỏi khác:

Khi nào tôi KHÔNG cần sử dụng các hàm hủy ảo?

Trong trường hợp nào tôi KHÔNG nên sử dụng các hàm hủy ảo?

Chi phí sử dụng các công cụ hủy ảo là bao nhiêu nếu tôi sử dụng nó ngay cả khi không cần thiết?


6
Và nếu lớp của bạn không được thừa kế thì sao? Nhìn vào nhiều lớp thư viện tiêu chuẩn, một số ít có chức năng ảo vì chúng không được thiết kế để kế thừa.
Một số lập trình viên anh chàng

4
Ngoài ra tôi nghĩ rằng trong hầu hết các trường hợp, các hàm hủy cần phải là ảo. Không. Không có gì. Chỉ những người lạm dụng quyền thừa kế (thay vì ủng hộ sáng tác) mới nghĩ như vậy. Tôi đã thấy toàn bộ ứng dụng chỉ với một số ít các lớp cơ sở và các hàm ảo.
Matthieu M.

1
@underscore_d Với các triển khai điển hình, sẽ có thêm mã được tạo cho bất kỳ lớp đa hình nào trừ khi tất cả các công cụ ngầm như vậy đã bị làm mờ và tối ưu hóa. Trong các ABI chung, điều này liên quan đến ít nhất một vtable cho mỗi lớp. Bố cục của lớp cũng phải thay đổi. Bạn không thể quay lại một cách đáng tin cậy một khi bạn đã xuất bản một lớp như một phần của một số giao diện công cộng, bởi vì việc thay đổi nó một lần nữa sẽ phá vỡ tính tương thích ABI, vì rõ ràng là rất tệ (nếu có thể) để dự đoán sự ảo hóa như các hợp đồng giao diện nói chung.
FrankHB

1
@underscore_d Cụm từ "tại thời gian biên dịch" là không chính xác, nhưng tôi nghĩ điều này có nghĩa là một hàm hủy ảo không thể tầm thường cũng như với constexpr được chỉ định, vì vậy việc tạo mã bổ sung rất khó tránh (trừ khi bạn hoàn toàn tránh phá hủy các đối tượng như vậy) nó sẽ ít nhiều gây hại cho hiệu suất thời gian chạy.
FrankHB

2
@underscore_d "Con trỏ" có vẻ như cá trích đỏ. Nó có thể là một con trỏ tới thành viên ( không phải là một con trỏ theo định nghĩa). Với ABI thông thường, Con trỏ tới thành viên thường không khớp với từ máy (như con trỏ thông thường) và việc thay đổi một lớp từ không đa hình sang đa hình thường sẽ thay đổi kích thước của con trỏ thành thành viên của lớp này.
FrankHB

Câu trả lời:


41

Nếu bạn thêm một hàm hủy ảo vào một lớp:

  • trong hầu hết (tất cả?) các triển khai C ++ hiện tại, mọi đối tượng của lớp đó cần lưu trữ một con trỏ vào bảng công văn ảo cho kiểu thời gian chạy và chính bảng công văn ảo đó đã thêm vào hình ảnh thực thi

  • địa chỉ của bảng công văn ảo không nhất thiết phải hợp lệ trên các quy trình, điều này có thể ngăn chia sẻ các đối tượng đó một cách an toàn trong bộ nhớ dùng chung

  • có một con trỏ ảo nhúng làm nản lòng việc tạo một lớp có bố cục bộ nhớ khớp với một số định dạng đầu vào hoặc đầu ra đã biết (ví dụ, do đó, một Price_Tick*mục tiêu có thể được nhắm trực tiếp vào bộ nhớ được căn chỉnh phù hợp trong gói UDP đến và được sử dụng để phân tích / truy cập hoặc thay đổi dữ liệu, hoặc sắp xếp newmột lớp như vậy để ghi dữ liệu vào một gói gửi đi)

  • Các lệnh gọi của hàm hủy có thể - trong một số điều kiện nhất định - phải được gửi đi hầu như và ngoài luồng, trong khi các hàm hủy không ảo có thể được đặt nội tuyến hoặc tối ưu hóa nếu tầm thường hoặc không liên quan đến người gọi

Đối số "không được thiết kế để được kế thừa từ" sẽ không phải là lý do thực tế cho việc không phải lúc nào cũng có hàm hủy ảo nếu nó không tệ hơn theo cách thực tế như đã giải thích ở trên; nhưng điều tệ hơn là đó là một tiêu chí chính khi phải trả chi phí: mặc định có một hàm hủy ảo nếu lớp của bạn được sử dụng làm lớp cơ sở . Điều đó không phải lúc nào cũng cần thiết, nhưng nó đảm bảo các lớp trong chế độ thừa kế có thể được sử dụng tự do hơn mà không có hành vi không xác định ngẫu nhiên nếu một hàm hủy lớp dẫn xuất được gọi bằng con trỏ hoặc tham chiếu lớp cơ sở.

"trong hầu hết các trường hợp hủy diệt cần phải là ảo"

Không phải như vậy ... nhiều lớp không có nhu cầu như vậy. Có rất nhiều ví dụ về việc không cần thiết phải liệt kê chúng, nhưng chỉ cần xem qua Thư viện tiêu chuẩn của bạn hoặc nói boost và bạn sẽ thấy có phần lớn các lớp không có kẻ hủy diệt ảo. Trong boost 1.53, tôi đếm 72 bộ hủy ảo trong số 494.


23

Trong trường hợp nào tôi KHÔNG nên sử dụng các hàm hủy ảo?

  1. Đối với một lớp cụ thể mà không muốn được thừa kế.
  2. Đối với một lớp cơ sở mà không xóa đa hình. Cả hai máy khách sẽ không thể xóa đa hình bằng cách sử dụng một con trỏ tới Base.

BTW,

Trong trường hợp nào nên sử dụng các hàm hủy ảo?

Đối với một lớp cơ sở với xóa đa hình.


7
+1 cho # 2, đặc biệt không xóa đa hình . Nếu hàm hủy của bạn không bao giờ có thể được gọi thông qua một con trỏ cơ sở, thì việc biến nó thành ảo là không cần thiết và dư thừa, đặc biệt là nếu lớp của bạn không ảo trước đó (vì vậy nó trở nên mới nổi với RTTI). Để bảo vệ chống lại bất kỳ người dùng nào vi phạm điều này, như Herb Sutter khuyên, bạn nên làm cho dtor của lớp cơ sở được bảo vệ và không ảo, để nó chỉ có thể được gọi bằng / sau một hàm hủy có nguồn gốc.
gạch dưới

@underscore_d imho rằng một điểm quan trọng mà tôi đã bỏ lỡ trong các câu trả lời, vì trong trường hợp thừa kế, trường hợp duy nhất mà tôi không cần một nhà xây dựng ảo là khi tôi có thể chắc chắn rằng nó không bao giờ cần thiết
trước đây là

14

Chi phí sử dụng các công cụ hủy ảo là bao nhiêu nếu tôi sử dụng nó ngay cả khi không cần thiết?

Chi phí giới thiệu bất kỳ hàm ảo nào cho một lớp (được kế thừa hoặc một phần của định nghĩa lớp) là chi phí ban đầu rất cao (hoặc không phụ thuộc vào đối tượng) của một con trỏ ảo được lưu trữ trên mỗi đối tượng, như vậy:

struct Integer
{
    virtual ~Integer() {}
    int value;
};

Trong trường hợp này, chi phí bộ nhớ là tương đối lớn. Kích thước bộ nhớ thực của một thể hiện lớp bây giờ sẽ thường trông như thế này trên các kiến ​​trúc 64 bit:

struct Integer
{
    // 8 byte vptr overhead
    int value; // 4 bytes
    // typically 4 more bytes of padding for alignment of vptr
};

Tổng cộng là 16 byte cho Integerlớp này, trái ngược với chỉ 4 byte. Nếu chúng tôi lưu trữ một triệu trong số này trong một mảng, chúng tôi sẽ sử dụng 16 megabyte bộ nhớ: gấp đôi kích thước của bộ đệm CPU L3 8 MB thông thường và lặp đi lặp lại qua một mảng như vậy có thể chậm hơn nhiều lần so với tương đương 4 megabyte không có con trỏ ảo là kết quả của lỗi bộ nhớ cache bổ sung và lỗi trang.

Tuy nhiên, chi phí con trỏ ảo này cho mỗi đối tượng không tăng với nhiều chức năng ảo hơn. Bạn có thể có 100 hàm thành viên ảo trong một lớp và chi phí cho mỗi cá thể vẫn là một con trỏ ảo.

Con trỏ ảo thường là mối quan tâm ngay lập tức hơn từ quan điểm trên không. Tuy nhiên, ngoài một con trỏ ảo cho mỗi phiên bản là chi phí cho mỗi lớp. Mỗi lớp có các hàm ảo tạo ra một vtablebộ nhớ lưu địa chỉ cho các hàm mà nó thực sự sẽ gọi (gửi ảo / động) khi thực hiện một cuộc gọi hàm ảo. Các vptrcá thể được lưu trữ sau đó trỏ đến lớp cụ thể này vtable. Chi phí này thường là một mối quan tâm ít hơn, nhưng nó có thể làm tăng kích thước nhị phân của bạn và thêm một chút chi phí thời gian chạy nếu chi phí này được trả một cách không cần thiết cho một nghìn lớp trong một cơ sở mã phức tạp, ví dụ: vtableChi phí này thực sự tăng tỷ lệ thuận với nhiều hơn và nhiều chức năng ảo trong hỗn hợp.

Các nhà phát triển Java làm việc trong các lĩnh vực quan trọng về hiệu năng hiểu rất rõ loại chi phí này (mặc dù thường được mô tả trong ngữ cảnh quyền anh), do loại do người dùng Java định nghĩa thừa hưởng từ một objectlớp cơ sở trung tâm và tất cả các chức năng trong Java đều là ảo (có thể ghi đè ) trong tự nhiên trừ khi được đánh dấu khác. Kết quả là, một Java Integertương tự có xu hướng yêu cầu 16 byte bộ nhớ trên các nền tảng 64 bit do kết quả của vptrsiêu dữ liệu kiểu này được liên kết cho mỗi cá thể và trong Java thường không thể bọc một cái gì đó giống như intmột lớp mà không phải trả thời gian chạy chi phí hiệu suất cho nó.

Sau đó, câu hỏi là: Tại sao c ++ không đặt tất cả các hàm hủy theo mặc định?

C ++ thực sự ưa thích hiệu năng với kiểu suy nghĩ "trả tiền khi bạn đi" và vẫn còn rất nhiều thiết kế điều khiển bằng phần cứng bằng kim loại được thừa hưởng từ C. Nó không muốn bao gồm một cách không cần thiết cho việc tạo vtable và công văn động cho mỗi lớp / trường hợp liên quan. Nếu hiệu suất không phải là một trong những lý do chính khiến bạn sử dụng ngôn ngữ như C ++, thì bạn có thể hưởng lợi nhiều hơn từ các ngôn ngữ lập trình khác ngoài kia vì rất nhiều ngôn ngữ C ++ kém an toàn và khó hơn so với lý tưởng thường có hiệu suất lý do chính để ủng hộ một thiết kế như vậy.

Khi nào tôi KHÔNG cần sử dụng các hàm hủy ảo?

Khá thường xuyên. Nếu một lớp không được thiết kế để kế thừa, thì nó không cần một hàm hủy ảo và cuối cùng sẽ chỉ phải trả một khoản chi phí lớn có thể cho thứ mà nó không cần. Tương tự như vậy, ngay cả khi một lớp được thiết kế để kế thừa nhưng bạn không bao giờ xóa các thể hiện của kiểu con thông qua một con trỏ cơ sở, thì nó cũng không yêu cầu một hàm hủy ảo. Trong trường hợp đó, một thực tiễn an toàn là xác định một hàm hủy không ảo được bảo vệ, như vậy:

class BaseClass
{
protected:
    // Disallow deleting/destroying subclass objects through `BaseClass*`.
    ~BaseClass() {}
};

Trong trường hợp nào tôi KHÔNG nên sử dụng các hàm hủy ảo?

Nó thực sự dễ dàng hơn để bao phủ khi bạn nên sử dụng các hàm hủy ảo. Thông thường, nhiều lớp hơn trong cơ sở mã của bạn sẽ không được thiết kế để kế thừa.

std::vector, chẳng hạn, không được thiết kế để kế thừa và thường không nên kế thừa (thiết kế rất run), vì điều đó sẽ dễ xảy ra vấn đề xóa con trỏ cơ sở này ( std::vectorcố tình tránh một hàm hủy ảo) ngoài các vấn đề cắt đối tượng vụng về nếu lớp dẫn xuất thêm bất kỳ trạng thái mới.

Nói chung, một lớp được kế thừa nên có một hàm hủy ảo ảo công khai hoặc một lớp được bảo vệ, không ảo. Từ C++ Coding Standards, chương 50:

50. Làm cho các hàm hủy lớp cơ sở thành công khai và ảo, hoặc được bảo vệ và không ảo. Để xóa, hoặc không xóa; đó là câu hỏi: Nếu việc xóa thông qua một con trỏ đến Cơ sở cơ sở nên được cho phép, thì hàm hủy của Base phải là công khai và ảo. Nếu không, nó nên được bảo vệ và không ảo.

Một trong những điều mà C ++ có xu hướng nhấn mạnh ngầm (bởi vì các thiết kế có xu hướng trở nên thực sự giòn và khó xử và thậm chí có thể không an toàn nếu không) là ý tưởng rằng kế thừa không phải là một cơ chế được thiết kế để sử dụng như một suy nghĩ sau. Đó là một cơ chế mở rộng với tính đa hình trong tâm trí, nhưng một cơ chế đòi hỏi tầm nhìn xa về nơi cần mở rộng. Kết quả là, các lớp cơ sở của bạn nên được thiết kế như là gốc của hệ thống phân cấp thừa kế, và không phải là thứ bạn kế thừa từ sau này như một suy nghĩ trước mà không có bất kỳ tầm nhìn xa nào như vậy.

Trong những trường hợp đơn giản là bạn muốn kế thừa để sử dụng lại mã hiện có, thành phần thường được khuyến khích mạnh mẽ (Nguyên tắc tái sử dụng tổng hợp).


9

Tại sao c ++ không đặt tất cả các hàm hủy theo mặc định? Chi phí lưu trữ thêm và cuộc gọi của bảng phương thức ảo. C ++ được sử dụng cho hệ thống, độ trễ thấp, lập trình rt trong đó điều này có thể là gánh nặng.


Công cụ phá hủy không nên được sử dụng ở vị trí đầu tiên trong các hệ thống thời gian thực cứng vì nhiều tài nguyên như bộ nhớ động không thể được sử dụng để cung cấp bảo đảm thời hạn mạnh mẽ
Marco A.

9
@MarcoA. Từ khi nào các hàm hủy có nghĩa là cấp phát bộ nhớ động?
chbaker0

@ chbaker0 Tôi đã sử dụng một 'thích'. Chúng đơn giản là không được sử dụng theo kinh nghiệm của tôi.
Marco A.

6
Cũng thật vô nghĩa khi bộ nhớ động không thể được sử dụng trong các hệ thống thời gian thực cứng. Thật là tầm thường khi chứng minh rằng một đống được cấu hình sẵn với các kích thước phân bổ cố định và một bitmap phân bổ sẽ phân bổ bộ nhớ hoặc trả về một điều kiện hết bộ nhớ trong thời gian cần thiết để quét bitmap đó.
MSalters

@msalters khiến tôi phải suy nghĩ: hãy tưởng tượng một chương trình trong đó chi phí của mỗi thao tác được lưu trữ trong hệ thống loại. Cho phép kiểm tra thời gian biên dịch bảo đảm thời gian thực.
Yakk

5

Đây là một ví dụ điển hình về việc khi nào không sử dụng công cụ hủy ảo: Từ Scott Meyers:

Nếu một lớp không chứa bất kỳ hàm ảo nào, đó thường là một dấu hiệu cho thấy nó không có nghĩa là được sử dụng làm lớp cơ sở. Khi một lớp không có ý định được sử dụng làm lớp cơ sở, làm cho hàm hủy ảo thường là một ý tưởng tồi. Xem xét ví dụ này, dựa trên một cuộc thảo luận trong ARM:

// class for representing 2D points
class Point {
public:
    Point(short int xCoord, short int yCoord);
    ~Point();
private:
    short int x, y;
};

Nếu một int ngắn chiếm 16 bit, một đối tượng Point có thể vừa với thanh ghi 32 bit. Hơn nữa, một đối tượng Điểm có thể được truyền dưới dạng số lượng 32 bit cho các hàm được viết bằng các ngôn ngữ khác như C hoặc FORTRAN. Tuy nhiên, nếu hàm hủy của Point được tạo ảo, thì tình huống sẽ thay đổi.

Thời điểm bạn thêm thành viên ảo, một con trỏ ảo được thêm vào lớp của bạn trỏ đến bảng ảo cho lớp đó.


If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class.Wut. Có ai còn nhớ Ngày cũ tốt lành, nơi chúng ta được phép sử dụng các lớp và kế thừa để xây dựng các lớp thành viên và hành vi có thể tái sử dụng mà không cần phải quan tâm đến các phương thức ảo không? Thôi nào, Scott. Tôi có được điểm cốt lõi, nhưng "thường" đó thực sự đang đạt tới.
gạch dưới

3

Một hàm hủy ảo thêm một chi phí thời gian chạy. Chi phí đặc biệt lớn nếu lớp không có bất kỳ phương thức ảo nào khác. Hàm hủy ảo cũng chỉ cần thiết trong một kịch bản cụ thể, trong đó một đối tượng bị xóa hoặc bị phá hủy thông qua một con trỏ tới một lớp cơ sở. Trong trường hợp này, hàm hủy của lớp cơ sở phải là ảo và hàm hủy của bất kỳ lớp dẫn xuất nào sẽ là ảo. Có một vài tình huống trong đó một lớp cơ sở đa hình được sử dụng theo cách mà hàm hủy không cần phải là ảo:

  • Nếu các thể hiện của các lớp dẫn xuất không được phân bổ trên heap, ví dụ chỉ trực tiếp trên ngăn xếp hoặc bên trong các đối tượng khác. (Ngoại trừ nếu bạn sử dụng bộ nhớ chưa được khởi tạo và toán tử vị trí mới.)
  • Nếu các thể hiện của các lớp dẫn xuất được phân bổ trên heap, nhưng việc xóa chỉ xảy ra thông qua các con trỏ đến lớp dẫn xuất nhất, ví dụ như có một std::unique_ptr<Derived>, và đa hình chỉ xảy ra thông qua các con trỏ và tham chiếu không sở hữu. Một ví dụ khác là khi các đối tượng được phân bổ sử dụng std::make_shared<Derived>(). Nó là tốt để sử dụng std::shared_ptr<Base>miễn là con trỏ ban đầu là a std::shared_ptr<Derived>. Điều này là do các con trỏ được chia sẻ có công văn động riêng cho các hàm hủy (deleter) không nhất thiết phải dựa vào một hàm hủy của lớp cơ sở ảo.

Tất nhiên, bất kỳ quy ước nào chỉ sử dụng các đối tượng theo những cách đã nói ở trên có thể dễ dàng bị phá vỡ. Do đó, lời khuyên của Herb Sutter vẫn có giá trị hơn bao giờ hết: "Các hàm hủy của lớp cơ sở phải là công khai và ảo, hoặc được bảo vệ và không ảo." Theo cách đó, nếu ai đó cố gắng xóa một con trỏ đến một lớp cơ sở với hàm hủy không ảo, thì rất có thể anh ta sẽ nhận được một lỗi vi phạm truy cập tại thời điểm biên dịch.

Sau đó, một lần nữa có các lớp không được thiết kế thành các lớp cơ sở (công khai). Đề nghị cá nhân của tôi là làm cho chúng finaltrong C ++ 11 trở lên. Nếu nó được thiết kế là một chốt vuông, thì rất có thể nó sẽ không hoạt động tốt như một chốt tròn. Điều này có liên quan đến sở thích của tôi về việc có hợp đồng thừa kế rõ ràng giữa lớp cơ sở và lớp dẫn xuất, đối với mẫu thiết kế NVI (giao diện không ảo), cho trừu tượng thay vì các lớp cơ sở cụ thể và sự ghê tởm của tôi về các biến thành viên được bảo vệ, trong số những thứ khác , nhưng tôi biết tất cả các quan điểm này đang gây tranh cãi ở một mức độ nào đó.


1

Khai báo một hàm hủy virtualchỉ cần thiết khi bạn có kế hoạch làm cho classkế thừa của mình . Thông thường các lớp của thư viện chuẩn (chẳng hạn std::string) không cung cấp hàm hủy ảo và do đó không có nghĩa là phân lớp.


3
Lý do là phân lớp + sử dụng đa hình. Một hàm hủy ảo chỉ được yêu cầu nếu cần độ phân giải động, đó là tham chiếu / con trỏ / bất cứ thứ gì cho lớp chủ thực sự có thể tham chiếu đến một thể hiện của lớp con.
Michel Billaud

2
@MichelBillaud thực sự bạn vẫn có thể có đa hình mà không cần các bộ ảo. Một dtor ảo CHỈ được yêu cầu để xóa đa hình, tức là gọi deletemột con trỏ đến một lớp cơ sở.
chbaker0

1

Sẽ có một chi phí hoạt động trong hàm tạo để tạo vtable (nếu bạn không có các hàm ảo khác, trong trường hợp đó, bạn VẤN ĐỀ, nhưng không phải lúc nào cũng nên có một hàm hủy ảo). Và nếu bạn không có bất kỳ chức năng ảo nào khác, nó làm cho đối tượng của bạn có kích thước con trỏ lớn hơn mức cần thiết. Rõ ràng, kích thước tăng lên có thể có tác động lớn đến các vật thể nhỏ.

Có một bộ nhớ bổ sung được đọc để có được vtable và sau đó gọi hàm indirectory qua đó, đó là phần trên của hàm hủy không ảo khi hàm hủy được gọi. Và tất nhiên, như một hệ quả, một mã bổ sung nhỏ được tạo cho mỗi lệnh gọi đến hàm hủy. Điều này là cho các trường hợp trình biên dịch không thể suy ra loại thực tế - trong những trường hợp nó có thể suy ra loại thực tế, trình biên dịch sẽ không sử dụng vtable, mà gọi trực tiếp hàm hủy.

Bạn nên có một hàm hủy ảo nếu lớp của bạn được dự định là lớp cơ sở, đặc biệt nếu nó có thể được tạo / hủy bởi một thực thể khác ngoài mã biết loại đó là gì khi tạo, thì bạn cần một hàm hủy ảo.

Nếu bạn không chắc chắn, hãy sử dụng hàm hủy ảo. Việc xóa ảo sẽ dễ dàng hơn nếu nó xuất hiện như một vấn đề hơn là cố gắng tìm lỗi gây ra bởi "trình hủy đúng không được gọi".

Nói tóm lại, bạn không nên có một hàm hủy ảo nếu: 1. Bạn không có bất kỳ chức năng ảo nào. 2. Không xuất phát từ lớp (đánh dấu nó finaltrong C ++ 11, theo cách đó trình biên dịch sẽ cho biết nếu bạn cố gắng rút ra từ nó).

Trong hầu hết các trường hợp, việc tạo và hủy không phải là một phần lớn thời gian sử dụng một đối tượng cụ thể trừ khi có "nhiều nội dung" (việc tạo chuỗi 1 MB rõ ràng sẽ mất một thời gian, vì ít nhất 1 MB dữ liệu cần được sao chép từ bất cứ nơi nào nó hiện đang nằm). Việc phá hủy chuỗi 1 MB không tệ hơn việc phá hủy chuỗi 150B, cả hai sẽ yêu cầu giải phóng bộ lưu trữ chuỗi và không nhiều thứ khác, vì vậy thời gian sử dụng thường giống nhau [trừ khi đó là bản dựng gỡ lỗi, trong đó việc giải quyết thường lấp đầy bộ nhớ với một "Mẫu độc" - nhưng đó không phải là cách bạn sẽ chạy ứng dụng thực sự của mình trong sản xuất].

Nói tóm lại, có một chi phí nhỏ, nhưng đối với các vật thể nhỏ, nó có thể tạo ra sự khác biệt.

Cũng lưu ý rằng trình biên dịch có thể tối ưu hóa việc tra cứu ảo trong một số trường hợp, do đó, đây chỉ là một hình phạt

Như mọi khi nói về hiệu suất, dấu chân bộ nhớ, và như vậy: Điểm chuẩn và hồ sơ và đo lường, so sánh kết quả với các lựa chọn thay thế và xem MOST của thời gian / bộ nhớ được sử dụng và không cố gắng tối ưu hóa 90% mã không chạy nhiều [hầu hết các ứng dụng có khoảng 10% mã có ảnh hưởng lớn đến thời gian thực hiện và 90% mã không ảnh hưởng nhiều]. Làm điều này ở mức tối ưu hóa cao, vì vậy bạn đã có lợi ích của trình biên dịch làm một công việc tốt! Và lặp lại, kiểm tra lại và cải thiện từng bước. Đừng cố tỏ ra thông minh và cố gắng tìm ra điều gì là quan trọng và điều gì không trừ khi bạn có nhiều kinh nghiệm với loại ứng dụng cụ thể đó.


1
"sẽ là một chi phí trong hàm tạo để tạo vtable" - vtable thường "được tạo" trên cơ sở mỗi lớp bởi trình biên dịch, với hàm tạo chỉ có chi phí lưu trữ một con trỏ tới nó trong đối tượng đang xây dựng.
Tony

Ngoài ra ... tôi hoàn toàn tránh việc tối ưu hóa sớm, nhưng ngược lại, You **should** have a virtual destructor if your class is intended as a base-classlà một sự đơn giản hóa quá mức - và sự bi quan sớm . Điều này chỉ cần thiết nếu bất cứ ai được phép xóa một lớp dẫn xuất thông qua con trỏ đến cơ sở. Trong nhiều tình huống, đó không phải là như vậy. Nếu bạn biết nó là, sau đó chắc chắn, phát sinh chi phí. Mà, btw, luôn được thêm vào, ngay cả khi các cuộc gọi thực tế có thể được giải quyết tĩnh bởi trình biên dịch. Mặt khác, khi bạn kiểm soát chính xác những gì mọi người có thể làm với các đối tượng của mình, điều đó không đáng giá
underscore_d
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.