La cosa migliore è catturare l'eccezione nel costruttore e settare un flag.

codice:
class A {
   public: 
       A() : p(0), k(0) {
           p = new int;  // questo funge

           k = new int;  // questo solleva una bad_alloc

       }

       ~A() {
           delete p;
           delete k;
       }

    private:
        int* p;
        int* k;

};
se l'eccezione non viene catturata nel costruttore, nessuno libera la memoria di p. In più l'esecuzione si arresta al momento dell'eccezione e l'oggetto rimane incompleto e il distruttore non è invocato.

Un modo un pò laborioso è il seguente.
codice:
class A {
   public: 
       A() : p(0), k(0),init_ok(false) {
           try {
               p = new int;  // questo funge
           } catch (std::bad_alloc& err) {
               p=0;
               init_ok = false;
           }

           try {
               k = new int;  // questo solleva una bad_alloc
           } catch (std::bad_alloc& err) {
              k=0; 
              init_ok = false;
           }
           init_ok = true;
       }

       ~A() {
           delete p;
           delete k;
       }
       bool ok() const { return init_ok;}
    private:
        int* p;
        int* k;
        bool init_ok;
};
In questo caso l'eccezione è catturata e si setta il flag per poi controllarlo in un punto esterno del codice. (Ed eventualmente lanciare una successiva eccezione).
Questo garantisce che il distruttore venga richiamato sempre.