1

Is such initialization very bad?

class A
{
public:
    ~A();
    A();

    B &b;
};

A::~A()
{
    delete &b;
}

A::A() :
     b(*(new B()))
{
}

All I want is to access member "b" without "->" operator. Also I can't make class B to be part of class A because class B is incomplete in A class' header.

UPDATE: Thanks for answers! If you need more info, class B here represents a "signal" (list of callbacks). And in 99% cases this class is just member of other class (not pointer or reference). But one particular class (A in my example) can't include class B header. Still I want this signal in class A to be connected and called like any other signal:

object->signal.connect(...);
object->signal();

With dot, not "->". Class A can't include class B header because there are template classes that inherit class A...

Sion0
  • 77
  • 7
  • Why do you want to access it without `->`? – tkausl Feb 28 '19 at 15:32
  • `Also I can't make class B to be part of class A because class B is incomplete in A class' header.` Forward declare it? – Rietty Feb 28 '19 at 15:33
  • 7
    That design is somewhere between weird and dangerous – Cory Kramer Feb 28 '19 at 15:33
  • @Rietty Forward declarations won't magically make a type complete. – George Feb 28 '19 at 15:33
  • 1
    If you use [this technique](https://stackoverflow.com/a/625801/4342498) you can have a reference in `A` and not bind it to something you dynamically initialize. – NathanOliver Feb 28 '19 at 15:33
  • @Rietty The current proposed "solution" implies `B` is already forward declared. – François Andrieux Feb 28 '19 at 15:34
  • Another note is that you should not be using `new` or `delete` if you don't need to. (You almost don't ever need to.) Just use smart pointers or normal references. You also should not be deleting a reference. – Rietty Feb 28 '19 at 15:34
  • @FrançoisAndrieux Right, thanks. Didn't realize it. – Rietty Feb 28 '19 at 15:34
  • @George Of course, thank you. – Rietty Feb 28 '19 at 15:34
  • Is this a purely syntactic question, or do you have a specific use case you are trying to tackle, what is the relationship/ responsibility between A and B, why does A need to completely hold B as a public by reference member? – Michael Feb 28 '19 at 15:35
  • `How about something like: `std::unique_ptr pb; B& b() { return *pb; }` ? – Jarod42 Feb 28 '19 at 15:37
  • You should know as well that dynamically allocated members are inherently cache unfriendly, they can be surprisingly nasty for performace. An L3 cache miss takes a significant amount of cpu time. – George Feb 28 '19 at 15:38
  • 1
    @CoryKramer : I'm currently maintaining a codebase that uses this, erm, "concept" as one of its "memory management techniques" (pre C++11, of course). This results in references all around that you don't know if you're supposed to `delete` or not. – Daniel Kamil Kozar Feb 28 '19 at 15:46
  • @Jarod - that's what I'd recommend, except with `const` if I really wish to preserve the semantics (obviously making the type non-movable): `std::unique_ptr const pb;` – Toby Speight Feb 28 '19 at 15:52
  • Thanks for your attention. I added some more info to my question but it seems like most people voted for finding another solution – Sion0 Feb 28 '19 at 16:01
  • Both `.` and `->` require the definition of the class they are applied to. – Maxim Egorushkin Feb 28 '19 at 16:11
  • Other classes are free to include both A and B... Its just class A can't include B... – Sion0 Feb 28 '19 at 16:14

2 Answers2

7

Having reference members breaks the semantics of the assignment operator: it doesn't reset the reference, unlike a pointer. It also breaks move constructor and assignment. And it is not possible to take the sizeof or address of a reference member.

In my opinion, reference members should never be used, since they don't add any value but only introduce limitations.

Bjarne Stroustrup explains in the "Design and Evolution of C++" that the references were introduced into C++ to allow operator overloading and efficient argument passing. This is the reason reference cannot be null (it refers to an expression operand or call argument) and have all these special non-value-type semantics. People using reference members are applying references to a problem references weren't designed to solve.

Initialising a reference with a result of an allocating new expression is asking for memory leaks, since reference (pseudo) destructor does nothing (it doesn't invoke delete for you).

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Might not be the right place for this argument, but both of your points why reference members are bad are countered by "doesn't matter if the class is only ever used via (smart) pointers", which may very well be the case for something not part of your public API. And unlike (smart) pointers, a reference member communicates the explicit guarantee that it is available (and non-null) for the entire lifetime of the class (dangling status does not matter because that's the same for pointer members). – Max Langhof Feb 28 '19 at 15:50
  • @MaxLanghof I do not see value in having reference members, only limitations. – Maxim Egorushkin Feb 28 '19 at 15:57
  • Thanks for opinion. All I wanted is to access this member without "->". I updated question to explain why. But it seems like I need other solution... – Sion0 Feb 28 '19 at 16:08
0

"Is such initialization very bad?" - no, it is not very bad. It is somewhat unusual and not idiomatic, and might get (quite) a few eyebrows raised. I'd say, it is moderately bad and doesn't seem to be necessary, at least given the information provided.

SergeyA
  • 61,605
  • 5
  • 78
  • 137