Initialiserar du på rätt sätt i C++

Initiering i Modern C++
Ett av de mest förbisedda, men samtidigt viktigaste ämnena i C++, är initiering. Hur vi väljer att initiera våra variabler påverkar inte bara korrektheten i programmet, utan även läsbarhet, säkerhet och förutsägbarhet. Med Modern C++ har vi fått en hel verktygslåda med förbättrade sätt att initiera värden — från enkla variabler till komplexa objekt och containrar. I denna artikel visar jag varför det är viktigt, samt illustrerar med flera konkreta exempel.
Varför initiering spelar roll
Alla som har jobbat i C++ ett tag vet att oinitierade variabler kan vara en tickande bomb. Ett vanligt exempel:
int count;
if (count > 0) { ... } // Undefined behaviour
Detta är både farligt och svårt att felsöka. Genom att alltid initiera variabler får vi mer robust kod och slipper subtila buggar.
Klassisk initiering
Traditionellt har man initierat med tilldelning eller med parenteser:
int a = 42;
double pi(3.1415);
std::string name = "Anna";
Detta fungerar, men med Modern C++ (från C++11 och framåt) föredrar man ofta uniform initialization med klamrar:
int a{42};
double pi{3.1415};
std::string name{"Anna"};
Det blir även bättre med auto
.
auto a{42};
auto pi{3.1415};
auto name = std::string{"Anna"};
Det ser kanske trivialt ut, men skillnaden är viktig. Med klammerinitiering undviker man vissa problem med narrowing conversions, t.ex.:
int x1 = 3.14; // Tillåtet, men förlorar information
int x2{3.14}; // Kompileringsfel: narrowing
STL-containrar
När vi initierar standardcontainrar, så blir det ofta mycket tydligare med klamrar:
#include <vector>
#include <string>
#include <map>
auto numbers = std::vector<int>{1, 2, 3, 5, 8, 13};
auto greetings = std::vector<std::string>{"Hej", "Tjena", "Goddag"};
auto scores = std::map<std::string, int>{
{"Anna", 92}, {"Bertil", 85}, {"Cecilia", 98}
};
Koden ovan är både kompakt och lätt att läsa. Alternativet hade varit ett antal push_back och insert-anrop.
Initiering av pekare och new
Med auto och klammerinitiering blir kod med dynamiska allokeringar också renare:
auto p1 = new int{42};
auto p2 = new std::string{"Modern C++"};
auto p3 = std::make_unique< std::vector<int> >(std::initializer_list<int>{1, 2, 3});
Särskilt när man använder smarta pekare (vilket man alltid bör göra nuförtiden) så är klamrar att föredra.
Konstruktor- och medlems initiering
När man skriver egna klasser, så kan man använda klammerinitiering även i konstruktorslistor:
class Person {
std::string name;
int age;
public:
Person(std::string n, int a) : name{std::move(n)}, age{a} {}
};
Från och med C++11 kan man också initiera medlemmar direkt vid deklarationen:
class Account {
std::string number{"ABC-123"};
int balance{0};
};
Detta gör det lättare att se default-värden, utan att behöva bläddra till konstruktorn.
Funktioner med initieringslistor
En elegant detalj med C++11 och framåt är att man kan använda klammerinitiering direkt i funktionsanrop:
void print_sum(std::initializer_list<int> values) {
auto sum = 0;
for (auto v : values) sum += v;
std::cout << "Summa: " << sum << "\n";
}
print_sum( {10, 20, 30, 40} );
Detta fungerar tack vare std::initializer_list
, som många STL-funktioner också stöder.
Strukturbindning + initiering
Kombinera initiering med strukturbindning (från C++17), och du får mycket elegant kod:
#include <tuple>
#include <string>
auto get_person() -> std::tuple<std::string, int> {
return {"Dora Explorer", 7};
}
auto [name, age] = get_person();
std::cout << name << " är " << age << " år\n";
Avslutande reflektioner
Modern C++ har förändrat hur vi skriver initieringskod — till det bättre. Med uniform initiering, auto, strukturbindningar och initializer_list blir våra program tydligare, säkrare och mer uttrycksfulla. Jag rekommenderar starkt att gå över till klammerinitiering i så stor utsträckning som möjligt.
Det är ett enkelt sätt att modernisera sin kodbas — och din framtida jag kommer att tacka dig.
Min katt, C++, föredrar också klammerinitiering. Men det är mest för att han gillar att ligga och sova på {}-tangenterna.