C ++ yapılarını başlatmanın doğru yolu


82

Kodumuz bir POD (Düz Eski Veri Yapısı) yapısı içerir (başlangıçta başlatılması gereken diğer yapıları ve POD değişkenlerini içeren temel bir c ++ yapısıdır.)

Ben kadarıyla birini Tabanlı okumak , bu gibi görünüyor:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

aşağıdaki gibi tüm değerleri sıfırlamalıdır:

myStruct = new MyStruct();

Bununla birlikte, yapı ikinci şekilde başlatıldığında, Valgrind daha sonra bu değişkenler kullanıldığında "koşullu sıçrama veya hareket başlatılmamış değer (ler) e bağlıdır" diye şikayet eder. Anlayışım burada kusurlu mu yoksa Valgrind yanlış pozitifler mi veriyor?


1
Ayrıca, new MyStruct()C ++ 03'te herhangi bir dolgu baytı ayarlamanıza gerek olmadığını unutmayın . C ++ 0x'de öyle. Herhangi bir dolgu biti C ++ 0x'de 0'a ayarlanacaktır.
Johannes Schaub -

Yakaladığınız için teşekkürler, sorumu düzenledim.
Shadow503


Anladığım kadarıyla bu hata mesajını almamalısın. Veri yapınız bir POD değil mi? Kodunuzu hala bu davranışı gösteren en küçük programa indirgeyebilir ve bu saf programı gönderebilir misiniz? (Bkz. Sscce.org ).
Robᵩ

Hangi derleyici? MyStruct'ın tanımını gönderebilir misiniz?
Alan Stokes

Yanıtlar:


121

C ++ 'da sınıflar / yapılar aynıdır (başlatma açısından).

POD olmayan bir yapı aynı zamanda bir kurucuya sahip olabilir, böylece üyeleri başlatabilir.
Yapınız bir POD ise, bir başlatıcı kullanabilirsiniz.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

Alternatif olarak, varsayılan kurucuyu kullanabilirsiniz.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Valgrind'in şikayetçi olduğuna inanıyorum çünkü C ++ eskiden böyle çalışıyordu. (C ++ 'ın sıfır başlatma varsayılan yapısıyla ne zaman yükseltildiğinden tam olarak emin değilim). Yapabileceğiniz en iyi şey, nesneyi başlatan bir kurucu eklemektir (yapılara izin verilen kurucular vardır).

Bir yan not olarak:
Birçok yeni başlayan init'e değer vermeye çalışır:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

"En Vexing Ayrıştırma" için hızlı bir arama, yapabileceğimden daha iyi bir açıklama sağlayacaktır.


1
C ++ 'da değişkenlerin bu sıfır başlatılması için bir referansınız var mı? Son duyduğumda, hala C. IIRC ile aynı davranışa sahip, MSVC derleyicisi sıfır başlatılmamış üyeleri bir noktada düşürdü (performans nedenleriyle, varsayıyorum), bu da görünüşe göre diğer MS bölümleri için oldukça ciddi sorunlar yarattı :)
Marc Mutz - mmutz

1
hayır, C()ama öyle görünüyor ki haklısın .
Marc Mutz - mmutz

4
POD sınıfına sahip T, T()sıfır sayıl subobjects başlatıldı hep vardır. Üst düzey başlatmanın adı C ++ 98'de C ++ 03'tekinden farklı olarak adlandırıldı ("varsayılan başlatma" ve "değer başlatma"), ancak POD'lar için son etki aynıdır. Hem C ++ 98 hem de C ++ 03, içindeki tüm değerlere sıfır olacaktır.
Johannes Schaub -

1
@RockyRaccoon C c();maalesef bir değişken bildirimi değil, bir işlevin ileri bildirimi ('c' adlı, parametre almayan ve C türü bir nesne döndüren bir işlev). Google: Most Vexing Parse
Martin York

1
@RockyRaccoon Buradaki ve yorumunuzda bağlantı verdiğiniz soru arasındaki fark; bu durumda, dönüştürücüde kullanılan hiçbir parametre olmamasıdır. Bu nedenle, derleyici bunu ayrıştırmakta ve değişken bildirimi ile bir işlevin ileri bildirimi arasındaki farkı söylemekte zorlanır. İleri işlev bildirimini seçer. Yapıcıda bir parametreye ihtiyacınız varsa, derleyici bunun herhangi bir sorun olmadan bir değişken bildirimi olduğunu görebilir. C c(4);Geçerli bir değişken bildirimidir.
Martin York

2

Bize söylediğine göre valgrind'de yanlış bir pozitif gibi görünüyor. newSözdizimi ()bu POD olduğunu varsayarak, nesne değer başlangıç durumuna gerekir.

Yapınızın bazı alt bölümlerinin aslında POD olmaması ve bu beklenen başlatmayı engellemesi mümkün mü? Kodunuzu hala valgrind hatasını işaretleyen gönderilebilir bir örnek haline getirebiliyor musunuz?

Alternatif olarak, derleyiciniz aslında POD yapılarına değer ataması yapmıyor olabilir.

Her durumda, muhtemelen en basit çözüm, yapı / alt parçalar için gerektiği gibi kurucu (lar) yazmaktır.


1

Bazı test kodu yazıyorum:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++ derleyin ve çalıştırın:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

Tamam, çok aşina değilim new sc;vs new sc();. Eksik parenlerin bir hata olduğunu düşünürdüm. Ancak bu, cevabın doğru olduğunu gösteriyor gibi görünüyor (varsayılan
kurucuyu kullandığınızda 0'a başlıyor

0

Yapınızda sahip olduğunuz üyeleri başlatmanız gerekir, örneğin:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

Bu durumda bir sınıf değil, bir yapı kullanıyorum.
Shadow503

Yapı bir sınıftır.
Yazımı düzenledi

2
POD için bunu yapmanıza gerek yok
Alan Stokes

0

Bir POD yapısı olduğundan, onu her zaman 0 olarak ayarlayabilirsiniz - bu, alanları başlatmanın en kolay yolu olabilir (bunun uygun olduğunu varsayarak).


1
Bir yapı veya bir sınıfın, memset yapma veya bir kurucu oluşturma becerisiyle ilgisi yoktur. Bkz. Örn. Stackoverflow.com/questions/2750270/cc-struct-vs-class
Erik

Normalde, oluşturucu (muhtemelen) başlatmayı yaptığı için, onu oluşturduktan sonra bir sınıfı oluşturmazsınız.
Scott C Wilson

1
C ++ 'da "sınıf" ve "yapı" arasında, varsayılan koruma (özel ve genel, sırasıyla) dışında hiçbir fark yoktur. Bir yapı, POD olmayan olabilir ve bir sınıf bir POD olabilir, bu kavramlar ortogonaldir.
Marc Mutz - mmutz

@mmutz ve @Erik - kabul etti - karışıklığı önlemek için cevabımı kısalttı.
Scott C Wilson

memset () en iyi seçenek değildir, bir POD yapısını sadece varsayılan ctor'unu çağırarak sıfır başlatabilirsiniz.
Nils

0

Bu bana en kolay yol gibi görünüyor. Yapı üyeleri, süslü ayraçlar '{}' kullanılarak başlatılabilir. Örneğin, aşağıdaki geçerli bir başlatmadır.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

C ++ 'da yapılar hakkında iyi bilgiler var - https://www.geeksforgeeks.org/structures-in-cpp/

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.