Önemsiz nesne için "bu" da yerleşimi yeni aramak güvenli midir?


20

Bu sorunun zaten birkaç kez sorulduğunu biliyorum, ancak bu özel durum için bir cevap bulamadım.

Diyelim ki herhangi bir kaynağa sahip olmayan ve boş yıkıcı ve varsayılan kurucuya sahip önemsiz bir sınıfım var. Sınıf içi başlatma ile bir avuç üye değişkeni vardır; onlardan biri değil const.

Yeniden başlatma ve deInitelle yazma yöntemi olmadan böyle bir sınıf nesnesi istiyorum . Böyle yapmak güvenli mi?

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

Onunla herhangi bir sorun göremiyorum - nesne her zaman geçerli durumda, thishala aynı adresi gösteriyor; ama bu C ++ emin olmak istiyorum.


2
Nesne sabitinin herhangi bir üyesi var mı?
NathanOliver

2
Bu geçerliyse, eşdeğer olur *this = A{};mu?
Kevin

2
@Amomum , yani tüm veri üyelerinin değerlerini geçici değerlerle değiştirerek geçici bir nesne oluşturmak ve bu nesneye atamak *this = A{};anlamına gelir . İstediğiniz budur ve (bence) yeni bir yerleşimden daha okunabilir olduğundan, bunun yerine bununla giderdim. this->operator=(A{});*this
Kevin

1
@Kevin oh, benim hatam, haklısın. Daha fazla - Bence kopya kaldırılırsa eşit olmalı?
Amomum

1
sınıfı kelimelerle açıklamak yerine, sadece tek bir yöntem değil, tam sınıf yazmak çok daha iyidir. bkz. sscce.org
BЈовић

Yanıtlar:


17

Yasallığına benzer şekilde delete this, thisbildiğim kadarıyla yeni yerleşime de izin verilir. Ayrıca, thisdaha sonra mevcut olan diğer işaretçilerin / referansların kullanılıp kullanılamayacağı konusunda birkaç kısıtlama vardır:

[Basic.life]

Bir nesnenin ömrü sona erdikten sonra ve işgal edilen nesnenin yeniden kullanılmasından veya serbest bırakılmasından önce, orijinal nesnenin bulunduğu depolama konumunda yeni bir nesne oluşturulur, orijinal nesneye işaret eden bir işaretçi, orijinal nesneye atıfta bulunulursa veya orijinal nesnenin adı otomatik olarak yeni nesneye işaret eder ve yeni nesnenin ömrü başladıktan sonra yeni nesneyi değiştirmek için kullanılabilir:

  • yeni nesnenin saklanması, orijinal nesnenin kapladığı saklama yerini tam olarak kaplar ve
  • yeni nesne orijinal nesne ile aynı türdedir (üst düzey cv niteleyicileri yoksayılır) ve
  • orijinal nesnenin türü sabit değil ve bir sınıf türü, türü sabit olan veya başvuru türü olan statik olmayan bir veri üyesi içermiyorsa ve
  • ne orijinal nesne ne de yeni nesne potansiyel olarak çakışan bir alt nesne değildir ([intro.object]).

İlk ikisi bu örnekten memnun, ancak son ikisinin dikkate alınması gerekecek.

Üçüncü nokta ile ilgili olarak, işlevin const-nitelikli olmadığı göz önüne alındığında, orijinal nesnenin const olmadığını varsaymak oldukça güvenli olmalıdır. Sabitlik kaldırılmışsa arıza arayan tarafındadır. Const / başvuru üyesi ile ilgili olarak, bu atanabilir olduğunu iddia ederek kontrol edilebilir düşünüyorum:

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

Tabii ki, atanabilirlik bir gereklilik olduğundan, bunun yerine sadece *this = {};aynı programı üretmeyi bekleyebileceğim kullanabilirsiniz. Belki de daha ilginç bir kullanım durumu *this, başka türdeki bir nesnenin hafızasını yeniden kullanmak olabilir ( thisen azından yeniden yorumlama + aklama olmadan kullanım gereksinimlerini ortadan kaldıracaktır ).

Benzer şekilde delete this, yeni yerleşim this, "güvenli" olarak tanımlanamaz.


İlginç. Tüm bu koşulların bazı tür özelliklerde static_assert ile karşılandığından emin olmak mümkün müdür? Const üyeleri hakkında bir tane olup olmadığından emin değilim ...
Amomum

1
@Amomum Subobject olmanın test edilebilecek bir şey olduğunu düşünmüyorum. Const veya referans üyeleri sınıfı önemsiz bir sınıf için başka türlü olabileceğini sanmıyorum.
eerorika

Bu, sabitlik konusunda sıkı bir örtüşme anlamına geliyor mu? bunun devreye girebileceği bir örnek verebilir misiniz?
darune

1
@ darune Yerleşim-yeni üzerine bir const nesnesi oluşturun, nesnenin orijinal adını (veya önceden var olan bir işaretçi veya refernce) kullanın ve davranış tanımsız olacaktır. Tamamen aynı olmasa da, sıkı yumuşatma optimizasyonu ile hemen hemen aynıdır.
eerorika

1
Ters delete ptrDİR new T(). Ters new(ptr)T{}DİR ptr->~T();. stackoverflow.com/a/8918942/845092
Mooing Ördek

7

Bunu kapsayan kurallar [basic.life] / 5

Bir program, nesnenin kapladığı depolamayı yeniden kullanarak veya yıkıcıyı sınıf türünde bir nesne için açıkça çağırarak herhangi bir nesnenin ömrünü sona erdirebilir. Sınıf türündeki bir nesne için, programın, nesnenin kapladığı depolama alanı yeniden kullanılmadan veya serbest bırakılmadan önce yıkıcıyı açıkça çağırması gerekmez; ancak, yıkıcıya açık bir çağrı yoksa veya depolamayı serbest bırakmak için bir silme ifadesi kullanılmazsa, yıkıcı dolaylı olarak çağrılmaz ve yıkıcı tarafından üretilen yan etkilere bağlı herhangi bir program tanımlanmamış bir davranışa sahiptir.

ve [basic.life] / 8

Bir nesnenin ömrü sona erdikten sonra ve işgal edilen nesnenin yeniden kullanılmasından veya serbest bırakılmasından önce, orijinal nesnenin bulunduğu depolama konumunda yeni bir nesne oluşturulur, orijinal nesneye işaret eden bir işaretçi, orijinal nesneye atıfta bulunulursa veya orijinal nesnenin adı otomatik olarak yeni nesneye işaret eder ve yeni nesnenin ömrü başladıktan sonra yeni nesneyi değiştirmek için kullanılabilir:

  • yeni nesnenin saklanması, orijinal nesnenin kapladığı saklama yerini tam olarak kaplar ve

  • yeni nesne orijinal nesne ile aynı türdedir (üst düzey cv niteleyicileri yoksayılır) ve

  • orijinal nesnenin türü sabit değil ve bir sınıf türü, türü sabit olan veya başvuru türü olan statik olmayan bir veri üyesi içermiyorsa ve

  • ne orijinal nesne ne de yeni nesne potansiyel olarak çakışan bir alt nesne değildir ([intro.object]).

Nesneniz önemsiz olduğu için [basic.life] / 5 hakkında endişelenmenize gerek yoktur ve [basic.life] / 8'deki mermi noktalarını tatmin ettiğiniz sürece güvenlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.