Why are begin() functions needed to initialize hardware (if they are)? I'm writing a program for an ATmega microcontroller using the Arduino framework.
While implementing an hardware-related class (one to manage LEDs), I put some hardware initialization code such as pinMode(OUTPUT) in the class constructor. Then I realized that in all Arduino libraries I know the hardware initialization is done in a begin() or init(), so I created one for my Led class as well and moved pinMode() there.

Now I'm wondering if I really should define such initialization funcitons, i.e. why they are needed - or, if they are not, why so many libraries have them. I played around a bit and found that for example this code works on an ATmega1284 (it makes the LED on pin 28 light up).

struct Test {
  Test() {
    pinMode(28, OUTPUT);
    digitalWrite(28, HIGH);
    // other stuff
  }
  // ...
};

Test test;

void setup() {
}

void loop() {
}


I presume that this code is somehow ill-conceived, letting alone the fact that it lights up a led without any code in the main function (or maybe that is exaclty the problem?), but if so I can not see why. What did I not consider?
Should the code above be rather writte as in the following snippet, as the existence of all these begin() functions leads me to believe?

struct Test {
  Test() {
    // other stuff
  }
  void begin() {
    pinMode(28, OUTPUT);
    digitalWrite(28, HIGH);
  }
  // ...
};

Test test;

void setup() {
  test.begin();
}

void loop() {
}
It's all to do with the order of execution.

Constructors are executed before normal functions. That includes the main() function which eventually calls setup() and (repeatedly) loop(). One of the other things that main() does is call init() (or some variant of) which configures the hardware.

Since that happens after any global constructors have been run anything that the constructor did to the hardware could potentially be undone.

Thus it is normal to defer any hardware configuration until after main() has done the work of setting things up, and the convention is to use a begin() function to do it.