2011-07-07 40 views
5

Tôi đọc một số C++ văn bản và có đoạn mã sau:Câu hỏi về các loại phù hợp trong C++?

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

void main() { 
    A* p1 = new B; // B may be larger than A :OK [Line 1] 
    B* p2 = new A; // B may be larger than A :Not OK [Line 2] 
} 

Tôi có 2 câu hỏi:

  1. Tôi không hiểu những gì tác giả có nghĩa là bằng cách bình luận ở Line 1 và Line 2
  2. Tại sao chúng ta không thể làm trong Dòng 2?
+1

Thực tế, tôi nghĩ rằng 'A' có thể lớn hơn' B', do tối ưu hóa lớp cơ sở trống và một số bố cục ABI lạ. –

Trả lời

8

Vâng, "lớn hơn" không phải là chìa khóa ở đây. Vấn đề thực sự là mối quan hệ "là một".

Bất kỳ đối tượng của class B cũng loại class A (class B cũng class A là do thừa kế), do đó, dòng đầu tiên là ổn (con trỏ để class A thể chỉ cũng trỏ đến một đối tượng của class B), nhưng nghịch đảo là không đúng (class A không phải là class B và thậm chí có thể không có ý tưởng về sự tồn tại class B), vì vậy dòng thứ hai sẽ không biên dịch.

0

Bởi vì B có nguồn gốc từ A, con trỏ B * không thể trỏ đến A.

0

B* p2 = new A; là không hợp lệ vì một con trỏ-to-B có thể mong đợi B để có nhiều thông tin hơn A.

Ví dụ:

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

B* p2 = new A; 
p2->notInA = 5; // Wait, which notInA are we talking about? 
       // p2 is really an A, and As don't have notInA! 
7

Tác giả được hiển thị cho bạn rằng ông không hiểu C++ (hoặc lập trình nói chung). Không có vấn đề về kích thước ("lớn hơn") tham gia. Vấn đề là B "isA" A, do đó, con trỏ đến A có thể được khởi tạo bằng con trỏ đến B. Nhưng điều ngược lại không đúng.

+0

Có rất nhiều sách C++ xấu xung quanh tôi khóc cho người mới bắt đầu không biết rõ hơn. –

1

Nhận xét thật ngớ ngẩn. Kích thước đối tượng không có nhiều việc phải làm với nó. Vấn đề là bạn có thể ẩn các loại con trỏ upcast, nhưng không downcast.

BTW, mainphải có loại trả lại là int. Không phải void.

0

Khi bạn đang sử dụng con trỏ, giá trị con trỏ là vị trí bộ nhớ. Điều này có nghĩa là bạn có thể đặt con trỏ trỏ tới BẤT K point điểm nào trong bộ nhớ. Để "dereference" mà bộ nhớ và làm việc với các đối tượng được lưu trữ tại thời điểm đó bạn cần phải biết loại đối tượng để mong đợi.
Khi lớp B kế thừa lớp A, B sẽ bao gồm "A". Sharptooth là chính xác là nói rằng có một "Is-A" mối quan hệ. "B" là "A", nhưng "A" không phải là "B".
Vấn đề sẽ là chính xác như nhau trong đoạn mã sau:

string* s = new string(""); 
int a = 44; 
s = (string*)&a; //compiler error if not cast 
cout << s; // randomness printed. 
0
class B : public A { }; 

A* p1 = new B; // B may be larger than A :OK [Line 1] 
B* p2 = new A; // B may be larger than A :Not OK [Line 2] 

Tôi không hiểu những gì tác giả có nghĩa là bằng cách bình luận ở Line 1 và Line 2.
Tại sao không chúng ta có thể làm gì trong Dòng 2?

class B có nguồn gốc từ class A, trong đó - từ quan điểm của các biến thành viên nó chứa - có nghĩa là nó có mọi thứ A có và bất cứ điều gì nó chọn để thêm chính nó. Trong mã đơn giản của bạn, B đã không thêm bất cứ điều gì, nhưng nếu nó đã có các thành viên dữ liệu bổ sung, nó rõ ràng sẽ yêu cầu bộ nhớ nhiều hơn để lưu trữ hơn loại A đơn giản hơn. Nếu nó thêm một hàm thành viên ảo trong đó A không có, trình biên dịch có thể được dự kiến ​​sẽ thêm một con trỏ trong B ghi lại địa chỉ của bảng công văn ảo liệt kê các địa chỉ của các hàm thành viên virtual của nó. Trình biên dịch cũng miễn phí để thêm đệm nếu nó cảm thấy thích nó.

Do đó, trường hợp chung là kích thước của một lớp dẫn xuất là >= kích thước của lớp cơ sở của nó.

A* p1 = new B; // B may be larger than A :OK [Line 1] 

Ở đây, tuy nhiên nhiều không gian B thực sự cần được phân bổ từ đống/miễn phí cửa hàng, và địa chỉ của bộ nhớ lưu trữ trong p1. Nếu B lớn hơn A, nó không có sự khác biệt - đó là một nơi khác anyway - điều quan trọng là một B* được đảm bảo để có thể được lưu trữ trong một A*.

B* p2 = new A; // B may be larger than A :Not OK [Line 2] 

Ở đây, một mới A đang được tạo trên heap, nhưng các lập trình viên đang cố gắng để nói với trình biên dịch rằng có một B tại địa chỉ đó. Trình biên dịch sẽ không tin nó (trừ khi bị ép buộc) - bạn sẽ đơn giản gặp lỗi thời gian biên dịch. Nếu bạn làm thế buộc các trình biên dịch (ví dụ p2 = (B *) (mới A) ) to treat the memory address in p2 as if it were an B , then it may later try to access additional data it expects to be part of any B which simply doesn't exist in any A`: thêm các thành viên dữ liệu, con trỏ văn ảo vv ..

1

Mọi người ở đây là đưa ra câu trả lời đúng, nhưng tôi muốn chỉ ra những gì tác giả có nghĩa là bằng cách "lớn hơn", vv
Hãy xem xét hai loại cổ phiếu này:

class Animal { 
    public: 
    bool bIsHungry; 
}; 

class Bird : public Animal { 
    public: 
    bool bIsFlying; 
} 

Sau đó, khi tôi gọi

Animal* animal = new Bird; // B may be larger than A :OK [Line 1] 

chương trình phân bổ đủ không gian để phù hợp với biến "bIsHungry" và biến "bIsFlying". (Tuy nhiên, trừ khi bạn định kiểu "động vật", bạn sẽ chỉ có thể truy cập "bIsHungry" mặc dù "bIsFlying" cũng được dành riêng cho "động vật" trong bộ nhớ.)

Khi bạn gọi

Bird* parrot = new Animal; // B may be larger than A :Not OK [Line 2] 

chương trình chỉ phân bổ đủ không gian để phù hợp với biến "bIsHungry". Tuy nhiên, người sử dụng "con vẹt" có thể muốn viết mã như

if(parrot->bIsFlying) 
{ //doSomething() 
    ... 
} 

này sẽ không làm việc, bởi vì với "Animal mới", chương trình chỉ được phân bổ không gian cho các lớp động vật, tức là "bIsHungry" và đã có không có bộ nhớ nào được phân bổ cho "bIsFlying". Trình biên dịch đã "xem" và sẽ "khiếu nại", tức là báo cáo lỗi.

+0

Cảm ơn rất nhiều vì một ví dụ rõ ràng. – ipkiss