Có an toàn khi gọi vị trí mới trên `this` cho đối tượng tầm thường không?


20

Tôi biết rằng câu hỏi này đã được hỏi nhiều lần nhưng tôi không thể tìm thấy câu trả lời cho trường hợp cụ thể này.

Giả sử tôi có một lớp tầm thường không sở hữu bất kỳ tài nguyên nào và có hàm hủy mặc định và hàm tạo mặc định. Nó có một số biến thành viên với khởi tạo trong lớp; không phải là một trong số đó là const.

Tôi muốn khởi tạo lại và phản đối lớp đó mà không cần viết deInitphương thức bằng tay. Có an toàn để làm điều đó như thế này?

void A::deInit()
{
  new (this)A{};
}

Tôi không thể thấy bất kỳ vấn đề nào với nó - đối tượng luôn ở trạng thái hợp lệ, thisvẫn trỏ đến cùng một địa chỉ; Nhưng đó là C ++ nên tôi muốn chắc chắn.


2
Có thành viên nào của const const không?
NathanOliver

2
Nếu điều này là hợp lệ, nó sẽ tương đương với *this = A{};?
Kevin

2
@Amomum *this = A{};có nghĩa là, this->operator=(A{});tức là tạo một đối tượng tạm thời và gán nó cho *this, thay thế các giá trị của tất cả các thành viên dữ liệu bằng các giá trị tạm thời. Vì đó là những gì bạn muốn và (theo ý kiến ​​của tôi) dễ đọc hơn một vị trí mới, thay vào đó tôi sẽ đi với điều đó.
Kevin

1
@Kevin oh, xấu của tôi, bạn nói đúng. Hơn - Tôi nghĩ rằng nó nên được bình đẳng nếu bản sao bị bỏ qua?
Amomum

1
thay vì giải thích lớp bằng từ, tốt hơn hết là viết lớp đầy đủ, và không chỉ một phương thức. xem sscce.org
BЈовић

Câu trả lời:


17

Tương tự như tính hợp pháp của delete this, vị trí mới thiscũng được cho phép theo như tôi biết. Ngoài ra, liên quan đến việc this, hoặc các con trỏ / tham chiếu có sẵn khác có thể được sử dụng sau đó, có một vài hạn chế:

[cơ bản. cuộc sống]

Nếu, sau khi thời gian tồn tại của một đối tượng kết thúc và trước khi lưu trữ mà đối tượng bị chiếm dụng được tái sử dụng hoặc giải phóng, một đối tượng mới được tạo tại vị trí lưu trữ mà đối tượng ban đầu chiếm giữ, một con trỏ trỏ đến đối tượng ban đầu, một tham chiếu được tham chiếu đến đối tượng ban đầu, hoặc tên của đối tượng ban đầu sẽ tự động đề cập đến đối tượng mới và, khi vòng đời của đối tượng mới bắt đầu, có thể được sử dụng để thao tác với đối tượng mới, nếu:

  • bộ lưu trữ cho đối tượng mới phủ chính xác vị trí lưu trữ mà đối tượng ban đầu chiếm giữ và
  • đối tượng mới cùng loại với đối tượng ban đầu (bỏ qua các vòng loại cv cấp cao nhất) và
  • loại của đối tượng ban đầu không đủ điều kiện const và, nếu một loại lớp, không chứa bất kỳ thành viên dữ liệu không tĩnh nào có loại đủ điều kiện const hoặc loại tham chiếu và
  • không phải đối tượng ban đầu cũng như đối tượng mới không phải là một tiểu dự án có khả năng chồng chéo ([intro.object]).

Hai cái đầu được thỏa mãn trong ví dụ này, nhưng hai cái cuối sẽ cần được xem xét.

Về điểm thứ ba, do hàm này không đủ điều kiện, nên khá an toàn khi cho rằng đối tượng ban đầu là không const. Lỗi nằm ở phía người gọi nếu chòm sao đã bị bỏ đi. Về const / thành viên tham chiếu, tôi nghĩ rằng có thể được kiểm tra bằng cách khẳng định rằng điều này có thể gán được:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

Tất nhiên, vì khả năng chuyển nhượng là một yêu cầu, thay vào đó bạn chỉ có thể sử dụng *this = {};cái mà tôi mong đợi để sản xuất cùng một chương trình. Một trường hợp sử dụng thú vị hơn có thể là sử dụng lại bộ nhớ *thischo một đối tượng thuộc loại khác (điều này sẽ không đáp ứng các yêu cầu sử dụng this, ít nhất là không diễn giải lại + giặt giũ).

Tương tự như delete this, vị trí mới thiskhó có thể được mô tả là "an toàn".


Hấp dẫn. Có thể đảm bảo tất cả các điều kiện này được thỏa mãn với static_assert trên một số đặc điểm loại không? Không chắc chắn nếu có một người về các thành viên const ...
Amomum

1
@Amomum Tôi không nghĩ việc trở thành tiểu dự án là điều có thể kiểm tra được. Các thành viên const hoặc tham chiếu sẽ làm cho lớp không thể gán được, điều mà tôi không nghĩ có thể xảy ra nếu không đối với một lớp tầm thường.
eerorika

điều này có nghĩa là răng cưa nghiêm ngặt có liên quan đến sự không đổi? bạn có thể cung cấp một ví dụ mà điều này có thể đi vào chơi?
darune

1
@darune Tạo một đối tượng const, vị trí mới trên nó, sử dụng tên gốc của đối tượng (hoặc một con trỏ hoặc giới thiệu có sẵn) và hành vi sẽ không được xác định. Khá giống nhau về hiệu quả như tối ưu hóa răng cưa nghiêm ngặt, nếu không nói là hoàn toàn giống nhau.
eerorika

1
Nghịch đảo delete ptrnew T(). Nghịch đảo new(ptr)T{}ptr->~T();. stackoverflow.com/a/8918942/845092
Vịt mướp

7

Các quy tắc bao gồm điều này là trong [basic. Life] / 5

Một chương trình có thể kết thúc thời gian tồn tại của bất kỳ đối tượng nào bằng cách sử dụng lại bộ lưu trữ mà đối tượng chiếm giữ hoặc bằng cách gọi rõ ràng hàm hủy cho một đối tượng thuộc loại lớp. Đối với một đối tượng của một loại lớp, chương trình không bắt buộc phải gọi hàm hủy một cách rõ ràng trước khi lưu trữ mà đối tượng chiếm giữ được sử dụng lại hoặc giải phóng; tuy nhiên, nếu không có lệnh gọi rõ ràng đến hàm hủy hoặc nếu biểu thức xóa không được sử dụng để giải phóng bộ lưu trữ, thì hàm hủy không được gọi ngầm và bất kỳ chương trình nào phụ thuộc vào các tác dụng phụ do hàm hủy tạo ra có hành vi không xác định.

[cơ bản. cuộc sống] / 8

Nếu, sau khi thời gian tồn tại của một đối tượng kết thúc và trước khi lưu trữ mà đối tượng bị chiếm dụng được tái sử dụng hoặc giải phóng, một đối tượng mới được tạo tại vị trí lưu trữ mà đối tượng ban đầu chiếm giữ, một con trỏ trỏ đến đối tượng ban đầu, một tham chiếu được tham chiếu đến đối tượng ban đầu, hoặc tên của đối tượng ban đầu sẽ tự động đề cập đến đối tượng mới và, khi vòng đời của đối tượng mới bắt đầu, có thể được sử dụng để thao tác với đối tượng mới, nếu:

  • bộ lưu trữ cho đối tượng mới phủ chính xác vị trí lưu trữ mà đối tượng ban đầu chiếm giữ và

  • đối tượng mới cùng loại với đối tượng ban đầu (bỏ qua các vòng loại cv cấp cao nhất) và

  • loại của đối tượng ban đầu không đủ điều kiện const và, nếu một loại lớp, không chứa bất kỳ thành viên dữ liệu không tĩnh nào có loại đủ điều kiện const hoặc loại tham chiếu và

  • không phải đối tượng ban đầu cũng như đối tượng mới không phải là một tiểu dự án có khả năng chồng chéo ([intro.object]).

Vì đối tượng của bạn là tầm thường, bạn không phải lo lắng về [basic. Life] / 5 và miễn là bạn thỏa mãn các gạch đầu dòng từ [basic.life] / 8, thì nó an toàn.

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.