Friend classes in C++

I copied the first two class definitions from a tutorial on friend classes that I found here: https://en.wikipedia.org/wiki/Friend_class. I understand how classes and friend classes work for the most part but their example really confused me. I understand that they are simply creating the constructor inside `class A` but how are on earth is the compiler letting them do the `(B b)` inside the parenthesis? "B" is a class whereas "b" appears to be the instantiation of the class as an object...but I don't understand why the compiler is allowing that to happen there. Why wouldn't it be A(class className){}? And why does the compiler even allow a declaration there? It doesn't make sense to have a declaration as a parameter...or am I just confused and the instantiation of class A is simply requiring an instantiated object of a class B?
EDIT: And if this is the case, why am I able to instantiate an instance of class A by providing it with an already-instantiated instance of class A instead of an instance of class B?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  using namespace std;
class B {
	friend class A; // A is a friend of B

private:
	int i;
};

class A {
public:
	A(B b) {
		b.i = 0; // legal access due to friendship
	}
};
Last edited on
Crusher21 wrote:
And why does the compiler even allow a declaration there? It doesn't make sense to have a declaration as a parameter.

That's simply the syntax for parameter declaration in C++ (and in C). No different than void foo(int n);

Crusher21 wrote:
why am I able to instantiate an instance of class A by providing it with an already-instantiated instance of class A instead of an instance of class B?

class A has a copy constructor (a constructor that takes a const A&). You didn't write it, but C++ wrote one for you.
A(class className){}

In your hypothetical example, where would you put the name of the object of className?

void foo(class className obj) actually is valid syntax, a holdover from C's rules on structs, but is unnecessary.

e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13

class A{ }; // class declaration + empty body

void foo(class A); // function declaration, specifying "class" is optional

int main()
{
    foo(A()); // calling foo with a temporary variable of type A
}

void foo(class A obj) { } // function implementation (with empty body for brevity)
             // again, specifying "class" is optional


As Cubbi pointed out, you can think of a class as just being a custom type.
So instead of "int i", you now have "B b".
Last edited on
In reading about friend classes, we learn that a friend has access to the private members of a separate class. Look at this code... See how class B has member int i. But there is no code to access i. But because class A is friend to class B, we can use class A to set value to b.i and even get the value of b.i.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

using namespace std;

class B {
	friend class A; // A is a friend of B

private:
	int i;

	
};

class A {
public:
	A(B &b) {
		b.i = 0; // legal access due to friendship
	}

	int geti(B b) {
		return b.i;
	}
};


int main() {
	B classb;
	A classa(classb);

	cout << classa.geti(classb) << "\n";

	system("pause");
	return 0;
}
Last edited on
Okay that all makes sense. The only thing I still don't understand is why I don't get any compilation errors when I provide it with a different class. The parameter specifies that it must receive an instantiated object of type class B. However, if I create an object of class A or any other class and attempt to pass that in as the parameter, the compiler sees no issues with it.
Passing in a class A will use the default copy constructor. But you shouldn't be able to pass in "any other class". Create a class C and try passing that in.
You're right, attempting to pass in a class C definitely didn't work. But I still don't quite understand why. If class A could use the default copy constructor when it really needed a class A (what exactly does this mean btw? Is it changing it's type from type class A to B?), why can't a class C use its copy constructor to achieve the same?
Is class C a friend to Class B? If so, then you could do the same.
I tried passing an instance of class C to class B's constructor after declaring that C was a friend of B inside B, but it still won't let me pass the instance of class C.
@Crusher21, I'm not sure I understand your question, but a default copy constructor for A is something like:
1
2
3
A(const A& a) {
    *this = a;
}

Unless you specifically "delete" it, it will exist and you will be able to do something like:
1
2
3
B b;
A a(b);
A x(a);  // uses the copy ctor (default or otherwise) 

You can delete the default copy ctor like this:
 
A(const A &) = delete;

If you do that, then A x(a) won't work.
This looks silly. Are we sure we want to keep doing this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

using namespace std;

class C {
    friend class B;
    
    int a;
    
};

class B {
	friend class A; // A is a friend of B

private:
	int i;
	
	public:
	B(C &c) {
		c.a = 5; // legal access due to friendship
	}
	
	int geta(C c) {
		return c.a;
	}
	
};

class A {
public:
	A(B &b) {
		b.i = 0; // legal access due to friendship
	}

	int geti(B b) {
		return b.i;
	}
};


int main() {
        C classc;
	B classb(classc);
	A classa(classb);

	cout << classb.geta(classc) << "\n";
	cout << classa.geti(classb) << "\n";

	system("pause");
	return 0;
}
Last edited on
I'm not sure why this is confusing me so much. If I pass it an instantiation of class C and the compiler doesn't mind, what happens if class C doesn't have all the necessary functions? Since it was expecting a class B with functions x,y, and z and got a class C instead, this should lead to problems.
I think I understand how the copy constructor works but I also don't understand how that could possibly allow you to pass in anything other than a class B instantiation. Or maybe...in your example where you had A x(a) , the previous line was A a(b) . Is A x(a) simply using the b from before? Since "a" had previously accepted "b" as the argument. Is that how that works?
It's not a "copy constructor" if it's class X taking in a different class Y as a parameter; it's just a regular constructor.

Class C has declared Class B as its friend. That means that class B can see class C's private members (heh). In other words, class C is giving permission to B.

Since it was expecting a class B with functions x,y, and z and got a class C instead, this should lead to problems.

A constructor or function's parameters don't care about what functions a parameter has, it cares about what type the parameter is. The functions that a parameter has is a consequence of its type.

I'm probably just making this more confusing.

I tried passing an instance of class C to class B's constructor after declaring that C was a friend of B inside B, but it still won't let me pass the instance of class C.

Why don't you show us, in code, what doesn't work, and we can tell you why.
In other words, it just cares that it's of type "class"?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
class A {
	friend class B;
	void print2()
	{
		cout << "Hello" << endl;
	}
};
class B {
public:
	string hello;

	B(class A myClass)
	{
		myClass.print2();
	}
};
class C {
	
};
int main()
{
	C jan;
	B newB(jan); //cannot pass "jan" in because "no instance of constructor" etc
	system("pause");
    return 0;
}

However, this works for some reason:
1
2
3
	A jan;
	B newB(jan); //now it works
	B anotherB(newB); //this works even though it's not of class A, it's of class B 
Last edited on
A copy constructor is used when you want to make more than one instance of the same class and you want to use the first instance to set value to the second instance.

By default all classes have a copy constructor and it works as simple as this...
1
2
B classb;
B classb2 = classb;


I declared two class B's one called classb and another called classb2 and used the built in copy constructor to make classb2 the same as classb.
How am I using the copy constructor when I am doing the following?
1
2
3
A jan;
B newB(jan); 
B anotherB(newB)

To me, it just looks like I'm giving it a different parameter, it doesn't look like I'm setting "anotherB" equal to "newB", unless secretly anotherB is getting "jan" because of pointers or something
1. First you are creating object called "jan" of type A, using A's default constructor.
2. Then, you create an object called "newB", of type B, using the B's constructor that takes in an A (line 13).
3. Then, you create an object called "anotherB", of type B, using B's copy constructor. If you don't define a copy constructor, the compiler will define one for you that copies each data member.

i.e. (3) is the same as if you defined
1
2
3
4
B(const B& b)
{
    hello = b.hello;
}
Last edited on
Okay I understand now. As someone else stated, I'm used to the copy constructor being used like this:
1
2
B classb;
B classb2 = classb;

I didn't know that you could call it for any class instantiation by simply passing in another instantiated object of the same class type as a parameter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>

using namespace std;

class B {
	friend class A; // A is a friend of B

private:
	int i;

	
};

class A {
public:
	A(B &b) {
		b.i = 0; // legal access due to friendship
	}

	int geti(B b) {
		return b.i;
	}
};


int main() {
	B classb;
	A classa(classb); //This is the constructor for A which takes a class B.

	B classb2 = classb; //This is one way to call copy constructor
	B classb3(classb); //This is another way to call copy constructor


	cout << classa.geti(classb2) << "\n";
	cout << classa.geti(classb3) << "\n";

	system("pause");
	return 0;
}
I didn't know that you could call it for any class instantiation by simply passing in another instantiated object of the same class type as a parameter.

As a general rule, the designers of the C++ standard try to make the language as consistent and logical as they can.

Since, in general, you pass arguments to constructors using the same syntax as for any other function call, it's safe to assume that principle also applies to copy constructors. It would be weird for them to take that syntax away for one specific type of constructor.
Topic archived. No new replies allowed.