Có một số điều khá riêng biệt đang diễn ra tại đây. Mã này là hợp lệ, tuy nhiên bạn đã đưa ra một giả định không chính xác trong câu hỏi của bạn. Bạn nói
"lợi nhuận B :: geta [...], sau đó theo sự phân công giá trị 20-tự"
(tôi nhấn mạnh) Đây không phải là chính xác. getA không không sửa đổi đối tượng. Để xác minh điều này, bạn có thể chỉ cần đặt const
trong chữ ký phương thức. Sau đó tôi sẽ giải thích đầy đủ.
A getA() const {
cout << this << " in getA() now" << endl;
return (((A)*this) = 20);
}
Vậy điều gì đang xảy ra ở đây? Nhìn vào số sample code của tôi (Tôi đã sao chép bảng điểm của tôi vào cuối câu trả lời này):
A a = 10;
Điều này tuyên bố A với hàm tạo. Khá thẳng. Đây dòng tiếp theo:
B b; b.val_ = 15;
B không có bất kỳ nhà thầu, vì vậy tôi phải viết trực tiếp cho thành viên val_ của nó (kế thừa từ A).
Trước khi chúng tôi xem xét các dòng tiếp theo, A c = b.getA();
, chúng ta phải rất cẩn thận xem xét các biểu hiện đơn giản hơn:
b.getA();
này không sửa đổi b
, mặc dù nó superfically có thể trông giống như nó.
Cuối cùng, mã mẫu của tôi in ra b.val_
và bạn thấy rằng nó bằng 15 vẫn. Nó đã không thay đổi thành 20. c.val_
đã thay đổi thành 20 tất nhiên.
Nhìn vào bên trong getA
và bạn thấy (((A)*this) = 20)
. Hãy chia nhỏ phần này:
this // a pointer to the the variable 'b' in main(). It's of type B*
*this // a reference to 'b'. Of type B&
(A)*this // this copies into a new object of type A.
Nó đáng để tạm dừng ở đây. Nếu đây là (A&)*this
hoặc thậm chí *((A*)this)
thì đó sẽ là một dòng đơn giản hơn. Nhưng đó là (A)*this
và do đó điều này tạo ra một đối tượng mới của loại A và sao chép các slice có liên quan từ b vào nó.
(Thêm: Bạn có thể hỏi cách sao chép lát. Chúng tôi có tài liệu tham khảo B&
và chúng tôi muốn tạo một A
mới.Theo mặc định, trình biên dịch tạo ra một hàm tạo bản sao A :: A (const A&)
. Trình biên dịch có thể sử dụng điều này vì một tham chiếu B&
có thể được đúc tự nhiên thành const A&
.)
Cụ thể this != &((A)*this)
. Điều này có thể gây bất ngờ cho bạn. (Giảm: Mặt khác this == &((A&)*this)
thường (tùy thuộc vào việc có virtual
phương pháp))
Bây giờ chúng ta có đối tượng mới này, chúng ta có thể nhìn vào
((A)*this) = 20
Điều này khiến số lượng vào giá trị mới này . Tuyên bố này không không ảnh hưởng đến this->val_
.
Sẽ là một lỗi khi thay đổi getA
sao cho nó trả lại A&
. Trước hết, giá trị trả lại của operator=
là const A&
và do đó bạn không thể trả lại giá trị này là A&
. Nhưng ngay cả khi bạn có const A&
làm kiểu trả về, thì đây sẽ là tham chiếu đến biến cục bộ tạm thời được tạo bên trong getA. Nó là không xác định để trả lại những thứ như vậy.
Cuối cùng, chúng ta có thể thấy rằng c
sẽ mất bản sao này được trả về bởi giá trị từ geta
A c = b.getA();
Đó là lý do tại sao các mã hiện tại, nơi geta trả về bản sao bởi giá trị, là an toàn và tốt -định nghĩa.
== Chương trình đầy đủ ==
#include <iostream>
using namespace std;
struct A {
int val_;
A() { }
A(int val) : val_(val) { }
const A& operator=(int val) {
cout << this << " in operator= now" << endl; // prove the operator= happens on a different object (the copy)
val_ = val;
return *this;
}
int get() { return val_; }
};
struct B : public A {
A getA() const {
cout << this << " in getA() now" << endl; // the address of b
return (((A)*this) = 20);
// The preceding line does four things:
// 1. Take the current object, *this
// 2. Copy a slice of it into a new temporary object of type A
// 3. Assign 20 to this temporary copy
// 4. Return this by value
} // legal? Yes
};
int main() {
A a = 10;
B b; b.val_ = 15;
A c = b.getA();
cout << b.get() << endl; // expect 15
cout << c.get() << endl; // expect 20
B* b2 = &b;
A a2 = *b2;
cout << b2->get() << endl; // expect 15
cout << a2.get() << endl; // expect 15
}
Là 'const A & operator = (int val) {val_ = val; } 'thiếu câu lệnh trả về? – ruakh
Vâng, không có 'B :: getB', nhưng' B :: getA' trả về một thể hiện của B (là một lớp con của A), không phải là một cá thể của A. Đó là mã rất mơ hồ, nhưng dường như, trên bề mặt, hoàn toàn hợp pháp. –
@ruakh: Có vẻ như vậy. :) – netcoder