Ý nghĩa của clang's -Wweak-vtables là gì?


78

Về cơ bản tôi không hiểu clang's -Wweak-vtables. Đây là những gì tôi quan sát được cho đến nay:

Trường hợp một: (kích hoạt cảnh báo)

class A {
    public:
    virtual ~A(){}        
};

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Trường hợp hai: (Không kích hoạt cảnh báo)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}

Trường hợp ba: (Không kích hoạt cảnh báo)

class A {
    public:
    virtual ~A();

};

A::~A(){}

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Trường hợp bốn: (Cảnh báo kích hoạt)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Trường hợp năm: (Không kích hoạt cảnh báo)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}

Trường hợp sáu: (Không kích hoạt cảnh báo)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}

Trường hợp bảy: (Không kích hoạt cảnh báo)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}

Cảnh báo chính xác là

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]

Vì vậy, rõ ràng, nếu tôi không khai báo một hàm ảo không nội tuyến trong một lớp, nó sẽ gây ra một số loại vấn đề nếu và chỉ khi tôi bắt nguồn từ nó và lớp dẫn xuất có một hàm hủy ảo.

Câu hỏi:

  1. Tại sao điều này là một vấn đề?
  2. Tại sao điều này được khắc phục bằng cách khai báo một hàm ảo? (Cảnh báo nói về định nghĩa)
  3. Tại sao cảnh báo không xảy ra khi tôi không xuất phát từ lớp?
  4. Tại sao cảnh báo không xảy ra khi lớp dẫn xuất không có trình hủy ảo?

Câu trả lời:


102

Nếu tất cả các virtualphương thức của lớp là nội tuyến, trình biên dịch không có cách nào để chọn đơn vị dịch để đặt một bản sao dùng chung duy nhất của vtable - thay vào đó, một bản sao của vtable phải được đặt trong mỗi tệp đối tượng cần nó. Trên nhiều nền tảng, trình liên kết có thể hợp nhất nhiều bản sao này, bằng cách loại bỏ các định nghĩa trùng lặp hoặc bằng cách ánh xạ tất cả các tham chiếu đến một bản sao, vì vậy đây chỉ là một cảnh báo.

Việc triển khai một virtualhàm ngoài dòng cho phép trình biên dịch chọn đơn vị dịch thực hiện phương thức ngoài dòng đó làm "nhà" cho các chi tiết triển khai của lớp và đặt bản sao dùng chung duy nhất của vtable trong cùng một đơn vị dịch . Nếu nhiều phương thức nằm ngoài dòng, trình biên dịch có thể đưa ra lựa chọn tùy ý về phương thức miễn là lựa chọn đó chỉ được xác định bởi khai báo của lớp; ví dụ: GCC chọn phương thức không nội tuyến đầu tiên trong thứ tự khai báo.

Nếu bạn không ghi đè bất kỳ phương thức nào của một lớp, thì virtualtừ khóa sẽ không có tác dụng quan sát được, vì vậy trình biên dịch không cần phải phát ra một vtable cho lớp đó. Nếu bạn không dẫn xuất từ Ahoặc nếu bạn không khai báo trình hủy của lớp dẫn xuất virtual, thì không có phương thức nào được ghi đè trong Avà do đó Avtable của sẽ bị bỏ qua. Nếu bạn khai báo một virtualphương thức ngoài dòng bổ sung để loại bỏ cảnh báo và cũng thực hiện điều gì đó ghi đè một phương thức trong đó A, việc triển khai phương thức không nội dòng virtual(và bản sao vtable đi kèm của nó) cần được cung cấp trong một đơn vị dịch được liên kết , nếu không liên kết sẽ không thành công vì thiếu vtable.


2
Nó sẽ làm gì khi các phương thức ngoài dòng (khác nhau) của cùng một lớp xảy ra trong một số TU khác nhau?
MM

2
@Matt, một phương thức ngoài dòng được chọn theo một số cách tùy ý nhưng xác định dựa trên khai báo của lớp và vtable kết thúc trong cùng một TU như việc triển khai phương thức. Tất cả các TU có liên quan đều nhìn thấy cùng một khai báo: thanh khai báo không nhất quán, chúng sẽ đồng ý về phương pháp đã chọn và do đó tập hợp phát ra chính xác một vtable.
Jeffrey Hantin

Câu trả lời này không giải thích trường hợp 6 và 7.
Leon

2
@JeffreyHantin Ý bạn là gì khi "Trình hủy của B không phải là ảo" ? Trình biên dịch được tạo (khai báo ngầm) hàm hủy Bphải là ảo (vì bộ hủy của lớp cơ sở đã được khai báo virtual).
Leon

2
@Leon, trả lời thông số kỹ thuật: "Một hàm hủy được mặc định và không được định nghĩa là đã xóa được định nghĩa ngầm khi nó được sử dụng odr (3.2) hoặc khi nó được mặc định rõ ràng sau lần khai báo đầu tiên." Không có gì trong trường hợp 6 hoặc 7 sử dụng hoặc mặc định rõ ràng ~B(), do đó định nghĩa mặc định không được trình biên dịch khởi tạo ngầm định. Làm sao nó có thể là ảo nếu nó không tồn tại? Sự tầm thường của các ví dụ đang gây ra một số hiệu ứng phản trực giác.
Jeffrey Hantin
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.