11

I'm writing a library in C++. I have two classes in my library, A and B. I want to hide the A() constructor from any code that references my library. I also want class B to be able to call the A() constructor.

I come from a C# background and remember little of my C++. In C#, I would simply declare the A() constructor as internal. I've read that the closest way to do this in C++ is a combination of friend declarations and forward-declarations. How do I do this? Here are my three files below:

A.h:

#pragma once
class A
{
    private:
        A();
};

B.h

#pragma once
class A;
class B
{
    public:
        A createA();
};

B.cpp:

#include "A.h"
#include "B.h"

A B::createA()
{
    A result; //cannot access private member declare in class 'A'
    return result;
}

I've tried adding this to A.h:

public: friend A createA();

I've instead tried adding this to A.h with a corresponding forward declaration:

public: friend A B::createA();

I've instead tried adding and extern class B; to A.h and making B a class like this:

public: friend class B;

I'm at a loss.

I think this might be easier if I have the B::createA() function return a pointer to an A object rather than an A object directly, but that won't do in my case. I am emulating a closed API and the API call returns an A object rather than a pointer.

user2023861
  • 8,030
  • 9
  • 57
  • 86

4 Answers4

9

You probably just need to drop the "extern" from your third attempt to turn it into a proper forward-declaration. Try:

A.h:

#pragma once
class B;
class A
{
    friend class B;
private:
    A();
};
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • The forward declaration is likely what he was missing. – Zac Howland Sep 09 '13 at 18:40
  • That was it. Thanks! Is it possible to make only the `createA()` function a friend, and not the whole class? – user2023861 Sep 09 '13 at 18:47
  • @user2023861: Yes it is. You basically would copy the entire function signature (scope included) and place `friend` in front of it. For example: `friend A B::CreateA();` – Zac Howland Sep 09 '13 at 18:50
  • @ZacHowland That won't work as `B` is only forward declared. And you can't define `B` before `A` since than you already need `A` since you are returning it from `createA`. – Daniel Frey Sep 09 '13 at 18:54
  • I was just about to say that it didn't compile. It's not a requirement that only the function is a friend. I'll keep the class as a friend. Thanks again! – user2023861 Sep 09 '13 at 18:57
  • @DanielFrey: You have to switch a couple things around (and you cannot do it inline), but yes, it can be done. See my edit below. – Zac Howland Sep 09 '13 at 19:03
  • @ZacHowland D'uh! Of course. I was also thinking it should work but I wanted to be sure so I tried it out and made a stupid typo. Yes, you are right, it can be done. – Daniel Frey Sep 09 '13 at 19:10
1

Unless absolutely necessary, you should have A construct itself (or have a factory that creates A). If you really want B to do it:

class B; // foward declared

class A
{
private:
    A() {}
    friend class B;
};

class B
{
public:
    A CreateA()
    {
        A a;
        return a;
    }
};

int main()
{
    B b;
    A a = b.CreateA();
    return 0;
}

Note: You must forward declare B before declaring it a friend in A.

If you want just the function as a friend:

class A;

class B
{
public:
    A CreateA();
};

class A
{
private:
    A() {}
    friend class A B::CreateA();
};

A B::CreateA()
{
    A a;
    return a;
}

int main()
{
    B b;
    A a = b.CreateA();
    return 0;
}
Zac Howland
  • 15,777
  • 1
  • 26
  • 42
1

You don't need the external keyword. Make it simple:

// In A.h

class B;  // Forward declaration

class A
{
    friend class B; // Make all the class B friend
    A();
};

// In B.h

class B
{
public:
    A createA() {}
};

Live Example.

Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62
0

You can make B a friend of A:

class A
{
private:
   A();
   friend class B;
};
Sergey K.
  • 24,894
  • 13
  • 106
  • 174