Tìm loại đối tượng trong C ++


147

Tôi có một lớp A và một lớp khác kế thừa từ nó, B. Tôi đang ghi đè một hàm chấp nhận một đối tượng loại A làm tham số, vì vậy tôi phải chấp nhận một A. Tuy nhiên, sau đó tôi gọi các hàm mà chỉ B có, vì vậy tôi muốn trả về false và không tiếp tục nếu đối tượng được truyền không thuộc loại B.

Cách tốt nhất để tìm ra loại đối tượng được truyền cho hàm của tôi là gì?

Câu trả lời:


162

Dynamic_cast nên thực hiện các mẹo

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

Các dynamic_casttừ khóa phôi một dữ liệu từ một con trỏ hoặc tham chiếu loại khác, thực hiện một kiểm tra thời gian chạy để đảm bảo tính hợp lệ của các diễn viên.

Nếu bạn cố gắng chuyển sang con trỏ đến một loại không phải là một loại đối tượng thực tế, kết quả của việc truyền sẽ là NULL. Nếu bạn cố gắng truyền để tham chiếu đến một loại không phải là một loại đối tượng thực tế, thì diễn viên sẽ ném bad_castngoại lệ.

Đảm bảo có ít nhất một hàm ảo trong lớp Base để làm cho Dynamic_cast hoạt động.

Chủ đề Wikipedia Thông tin loại thời gian chạy

RTTI chỉ có sẵn cho các lớp đa hình, có nghĩa là chúng có ít nhất một phương thức ảo. Trong thực tế, đây không phải là một giới hạn bởi vì các lớp cơ sở phải có một hàm hủy ảo để cho phép các đối tượng của các lớp dẫn xuất thực hiện dọn dẹp đúng cách nếu chúng bị xóa khỏi một con trỏ cơ sở.


1
Bạn có ý nghĩa gì với việc phải có một hàm ảo trong lớp Cơ sở để làm cho Dynamic_cast hoạt động. Điều đó đối với tôi quan trọng, mà tôi sẽ chỉ đoán.
GiCo

3
OK tìm thấy nó: Thông tin loại thời gian chạy (RTTI) chỉ có sẵn cho các lớp đa hình, có nghĩa là chúng có ít nhất một phương thức ảo. Dynamic_cast và typeid cần RTTI.
GiCo

Không dynamic_castném nếu nó không chuyển đổi? Có cách nào để làm điều đó mà không tạo ra một cú ném?
jww

A* aptr = dynamic_cast<A*>(ptr);// không phải là như thế này
Mehdi Karamosly

Điều này có làm việc cho các POD đã được chuyển thành a uint8_t*không? Đó là, tôi có thể kiểm tra uint32_t* x = dynamic_cast<uint32_t*>(p), nơi puint8_t*? (Tôi đang cố gắng tìm một thử nghiệm cho các vi phạm lén lút).
jww

157

Diễn viên động là tốt nhất cho mô tả vấn đề của bạn, nhưng tôi chỉ muốn thêm rằng bạn có thể tìm thấy loại lớp với:

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
Tốt nếu bạn thực sự không biết đối tượng của bạn là gì. Câu trả lời được chấp nhận giả định bạn làm.
bao gồm

4
@xus Vâng. nó là một phần của tiêu đề std
Amey Jah

8
Tôi không thấy thế nào. Tên id loại không bắt buộc phải hữu ích và được xác định thực hiện.
Giày

3
Thú vị nhất ở đây: Tên của các thể hiện của cùng một lớp không phải bằng nhau. Tuy nhiên, bản thân loại phải so sánh bằng nhau cho các thể hiện của cùng một lớp, xem stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
Lưu ý gcc trả về tên magled, vd 11MyClass. Để tháo gỡ, bạn có thể sử dụng thư viện tiện ích mở rộng ABI cxxabi.h. Điều này mang lại cho bạn abi::__cxa_demanglecái sẽ cho bạn tên thật
David G

27

Điều này được gọi là RTTI , nhưng bạn gần như chắc chắn muốn xem xét lại thiết kế của mình ở đây, bởi vì việc tìm loại và làm những điều đặc biệt dựa trên nó làm cho mã của bạn dễ vỡ hơn.


4
Thật. Thật không may, tôi đang làm việc trên một dự án hiện có nên tôi thực sự không thể thay đổi thiết kế, hoặc bất cứ điều gì trong lớp A.
lemnisca

11

Để hoàn thành, tôi sẽ xây dựng bản dựng Robocide và chỉ ra rằng typeidcó thể sử dụng một mình mà không cần sử dụng tên ():

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

Đầu ra:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

Có thể nhúng vào các đối tượng của bạn một thẻ "ID" và sử dụng nó để phân biệt giữa các đối tượng của lớp A và các đối tượng của lớp B.

Điều này tuy nhiên cho thấy một lỗ hổng trong thiết kế. Lý tưởng nhất là các phương thức trong B mà A không có, nên là một phần của A nhưng để trống và B ghi đè lên chúng. Điều này không đi cùng với mã cụ thể của lớp và theo tinh thần của OOP.



4

Bởi vì lớp học của bạn không phải là đa hình. Thử:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

Bây giờ BaseClaslà đa hình. Tôi đã thay đổi lớp thành struct vì các thành viên của struct được mặc định công khai.


3

Mô tả của bạn là một chút bối rối.

Nói chung, mặc dù một số triển khai C ++ có cơ chế cho nó, bạn không cần phải hỏi về loại. Thay vào đó, bạn phải thực hiện một Dynamic_cast trên con trỏ tới A. Điều này sẽ làm là khi chạy, nội dung thực tế của con trỏ tới A sẽ được kiểm tra. Nếu bạn có B, bạn sẽ đưa con trỏ đến B. Nếu không, bạn sẽ nhận được một ngoại lệ hoặc null.


1
Cần lưu ý bạn sẽ nhận được một ngoại lệ duy nhất nếu bạn thực hiện một dàn diễn viên tài liệu tham khảo mà thất bại. tức là động_cast <T &> (t). Con trỏ phôi không trả về NULL. tức là Dynamic_cast <T *> (t)
AlfaZulu

Phải, tôi nên làm rõ điều đó tốt hơn. Cảm ơn. Tôi ước có một từ mô tả trong các loại C là tham chiếu phụ chứ không phải theo giá trị.
Uri

3

Như những người khác chỉ ra, bạn có thể sử dụng Dynamic_cast. Nhưng nói chung, sử dụng Dynamic_cast để tìm ra loại lớp dẫn xuất mà bạn đang làm việc chỉ ra thiết kế xấu. Nếu bạn đang ghi đè một hàm lấy con trỏ của A làm tham số thì nó có thể hoạt động với các phương thức / dữ liệu của chính lớp A và không nên phụ thuộc vào dữ liệu của lớp B. Trong trường hợp của bạn thay vì ghi đè nếu bạn chắc chắn rằng phương thức bạn đang viết sẽ chỉ hoạt động với lớp B, sau đó bạn nên viết một phương thức mới trong lớp B.


1

Sử dụng các chức năng quá tải. Không yêu cầu hỗ trợ Dynamic_cast hoặc thậm chí RTTI:

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

Ngay từ câu hỏi ban đầu: "Sau này tôi gọi các hàm chỉ có B" - quá tải sẽ hoạt động như thế nào trong trường hợp đó?
Marcin Gil

Khi bạn gọi Bar bằng A, không có nội dung B nào xảy ra. Khi bạn gọi Bar bằng B, các phương thức chỉ tồn tại trên B có thể được gọi. Bạn có đọc câu hỏi ban đầu không? Bar là "Tôi đang ghi đè một hàm chấp nhận một đối tượng loại A làm tham số"
jmucchiello

7
Điều này không hoạt động với đa hình động, mà tôi nghi ngờ người hỏi đang sử dụng. C ++ không thể chọn quá tải dựa trên lớp thời gian chạy của tham số, chỉ dựa trên loại thời gian biên dịch.
Steve Jessop

1

Nếu bạn có thể truy cập thư viện boost, có thể hàm type_id_with_cvr () là thứ bạn cần, có thể cung cấp kiểu dữ liệu mà không cần loại bỏ const, volility, & & & modifier . Đây là một ví dụ đơn giản trong C ++ 11:

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

Hy vọng điều này là hữu ích.

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.