一、友元
1、为什么需要友元这个概念
想要从外部,这里是通过Add函数访问私有数据成员,又不想通过接口(共有函数)去访问 可能有人说友元破坏了类的封装性,但还是看设计时的需求和要求。
class A
{
public:
A(int i = 0):m_i(i){}
friend void Add(A& a,A& b);
protected:
int m_j;
private:
int m_i;
};
void Add(A& a,A& b)
{
cout << a.m_i << endl;
cout << b.m_i << endl;
cout << a.m_i + b.m_i << endl;
}
void main()
{
A a(5);
A b(8);
Add(a,b);
}
运行结果:
2、什么是友元
友元 – 让函数或者类作为另一个类的朋友,则可以访问当前类的private或者protected成员 是单向的
友元 friend 机制允许一个类授权其他的函数访问它的非公有成员。
友元声明以关键字 friend 开头,它只能出现在类的声明中,它们不受其在类体中的public private 和 protected 区的影响.
友元分为外部函数友元,成员函数友元,类友元。
友元的特点: 1)不具有对称性:A是B的友元.并不意味着B是A的友元。 2)不具有传递性:A是B的友元,B是C的友元,但A不是C的友元。 3)不具有继承性::Base类型继承Object类型,如果Object类型是A的友元,但Base类型不是A友元。
3、友元的三种情况:
1)一个普通函数作为类的友元,那么在当前函数中就可以通过对象访问类的private或者protected
注意:这个函数只能在外部定义,在当前类中引用即可
代码示例:
class B;
class C;
class A
{
public:
A(int i = 0):m_i(i){}
friend int Sum(A& a,B& b,C& c);
private:
int m_i;
};
class B
{
public:
B(int j = 0):m_j(j){}
friend int Sum(A& a,B& b,C& c);
private:
int m_j;
};
class C
{
public:
C(int k = 0):m_k(k){}
friend int Sum(A& a,B& b,C& c);
private:
int m_k;
};
int Sum(A& a,B& b,C& c)
{
return a.m_i + b.m_j + c.m_k;
}
void main()
{
A a(10);
B b(20);
C c(30);
cout << Sum(a,b,c) << endl;
}
运行结果:
2)一个类的成员函数最为另一个类的友元
注意:成员函数建议放在类外定义,
class A;
class C;
class B
{
public:
B(int j = 0):m_j(j){}
void Sub(A& a,C &c);
private:
int m_j;
};
class A
{
public:
A(int i = 0):m_i(i){}
friend void B::Sub(A& a,C &c);
private:
int m_i;
};
class C
{
public:
C(int k = 0):m_k(k){}
friend void B::Sub(A& a,C &c);
private:
int m_k;
};
void B::Sub(A& a,C &c)
{
cout << a.m_i - c.m_k - m_j << endl;
}
void main()
{
A a(40);
B b(20);
C c(10);
b.Sub(a,c);
}
运行结果:
3)一个类作为类外一个类的友元类
一个类A作为类外一个类B的友元类,则A的所有成员函数就可以访问B的私有数据成员 使用情况示例:当前类内函数需要大量使用另一个类的私有数据成员
class B;
class C;
class A
{
public:
A(int a = 0) :m_a(a) {}
void print(B& b);
void test(B& b);
void show(B& b);
void print(C& c);
private:
int m_a;
};
class B
{
public:
B(int b = 0) :m_b(b) {}
friend class A;
private:
int m_b;
};
class C
{
public:
C(int c = 0) :m_c(c) {}
friend class A;
private:
int m_c;
};
void A::print(B& b)
{
cout << "A::print b "<<b.m_b << endl;
}
void A::test(B& b)
{
cout << "A::test "<<b.m_b << endl;
}
void A::show(B& b)
{
cout << "A::show " << b.m_b << endl;
}
void A::print(C& c)
{
cout << "A::print c "<<c.m_c << endl;
}
void main()
{
B b(30);
A a(40);
C c(10);
a.print(b);
a.show(b);
a.test(b);
a.print(c);
}
运行结果:
B和A互为朋友:
class B;
class C;
class A
{
public:
A(int a = 0) :m_a(a) {}
void print(B& b);
void test(B& b);
void show(B& b);
void print(C& c);
friend class B;
private:
int m_a;
};
class B
{
public:
B(int b = 0) :m_b(b) {}
friend class A;
void Print(A& a);
private:
int m_b;
};
void B::Print(A& a)
{
cout << "B::print a "<< a.m_a << endl;
}
class C
{
public:
C(int c = 0) :m_c(c) {}
friend class A;
private:
int m_c;
};
void A::print(B& b)
{
cout << "A::print b "<<b.m_b << endl;
}
void A::test(B& b)
{
cout << "A::test "<<b.m_b << endl;
}
void A::show(B& b)
{
cout << "A::show " << b.m_b << endl;
}
void A::print(C& c)
{
cout << "A::print c "<<c.m_c << endl;
}
void main()
{
B b(30);
A a(40);
C c(10);
a.print(b);
a.show(b);
a.test(b);
a.print(c);
b.Print(a);
}
运行结果:
4、友元注意点:
1)友元是单向的
2)友元是不能传递的
3)友元是不能继承的
对于以下代码: BB是AA的朋友,CC是BB的朋友,如果CC没有作为AA的友元类,则CC和AA没有关系 — 即友元不能传递 CC能不能访问AA的私有数据成员? 不能
class BB;
class CC;
class AA
{
public:
friend class BB;
private:
int m_a;
};
class BB
{
public:
friend class CC;
void Show(AA& a);
private:
int m_b;
};
class CC
{
public:
void print(BB& b);
void test(AA& a);
};
void CC::test(AA& a)
{
}
void BB::Show(AA& a)
{
cout << a.m_a << endl;
}
void CC::print(BB& b)
{
cout << b.m_b << endl;
}
void main()
{
BB b;
CC c;
c.print(b);
}
运行结果:
运行 cout << a.m_a << endl; 时会报错:
5、相关应用 – 实现 cout << a + b << endl; 的重载实现(a,b为类)
1)欲实现代码(实现两个不同类的相加):
class A
{
public:
A(int i = 0):m_i(i){}
private:
int m_i;
};
class B
{
public:
B(int j = 0):m_j(j){}
private:
int m_j;
};
void main()
{
A a(10);
B b(20);
cout << a + b << endl;
}
当前实现不了
其中cout << a + b << end; 可解读为//a.+(b) 或 +(a,b)
2)两个不是同一类型的对象要进行 + 的前提:
只有两个类其中的数据成员的个量、数据成员的类型一致的情况下,才能去尝试去进行相加。
3)代码实现示例:
class B;
class A
{
public:
A(int i = 0):m_i(i){}
int operator+(B& b);
private:
int m_i;
};
class B
{
public:
B(int j = 0):m_j(j){}
friend int A::operator+(B& b);
private:
int m_j;
};
int A::operator+(B& b)
{
return m_i + b.m_j;
}
void main()
{
A a(10);
B b(20);
cout << a + b << endl;
}
运行结果:
6、运算符重载重载成友元形式
一般情况下运算符重载重载成友元形式是以下这种情况
对于以下基本的+运算符重载示例
注意: 重载成成员函数可以将第一个操作数省略, 重载成友元函数不能省略第一个操作数,是几个操作数就写几个操作数
重载成成员函数形式 – a.+(b)
class A
{
public:
A(int i = 0):m_i(i){}
void print()
{
cout << m_i << endl;
}
A operator+(A& b)
{
return m_i + b.m_i;
}
private:
int m_i;
};
void main()
{
A a(5);
A b(10);
(a + b).print();
}
运行结果:
重载成 普通函数 – +(a,b) ,这里重载成友元函数
友元必须将两个参数都有
class A
{
public:
A(int i = 0):m_i(i){}
void print()
{
cout << m_i << endl;
}
A operator+(A& b)
{
return m_i + b.m_i;
}
friend A operator-(A& a,A& b);
private:
int m_i;
};
A operator-(A& a,A& b)
{
return a.m_i - b.m_i;
}
void main()
{
A a(5);
A b(10);
(a + b).print();
(a - b).print();
}
运行结果:
前置++和后置++的不同重载
class A
{
public:
A(int i = 0):m_i(i){}
void print()
{
cout << m_i << endl;
}
A operator+(A& b)
{
return m_i + b.m_i;
}
friend A operator-(A& a,A& b);
A operator++(int)
{
int t = m_i;
m_i = m_i + 1;
return t;
}
friend A& operator++(A& a);
private:
int m_i;
};
A& operator++(A& a)
{
++a.m_i;
return a;
}
A operator-(A& a,A& b)
{
return a.m_i - b.m_i;
}
void main()
{
A a(5);
A b(10);
(a++).print();
(++b).print();
}
运行结果:
7、对输出运算符cout的重载实现
欲实现 A a(5); cout<< a <<endl;`
1)思考:
怎么去重载输出运算符?类比于(a + b).print();//a.+(b) +(a,b) 重载输出时返回类型是什么类型的? 输出能不能作为左边,是值返回还是引用返回?
对于代码:`cout<< a <<endl;``
可以解析为:cout.<<(a) 或者 <<(cout,a)
2)相关知识点
cout -是- ostream类的对象,输出流类 cin -是- istream类的对象,输入流类
对于 cout << a;
一般情况下运算符可以解析成下面两种形式
(1)cout.<<(a) ostream类中重载了<<,程序员不能修改 (2)<<(cout,a) 可以在程序员自己定义的类中将<<重载成友元
返回值为引用
对于 cout<<a<<b; cout<<a 这个表达式作为了上面的表达式<<左边 、 即(cout<<a)<<b 所以返回值为引用
返回值为ostream类型
(cout<<a) << b 表达式执行完后,还能继续用<<,说明返回值为ostream类型,因为只有ostream类型才能使用<<
3)使用模板
friend ostream& operator<<(ostream &out,A &a)
4)使用示例
class A
{
public:
A(int i = 0):m_i(i){}
void print()
{
cout << m_i << endl;
}
A operator+(A& b)
{
return m_i + b.m_i;
}
friend ostream& operator<<(ostream& out, A&a);
private:
int m_i;
};
ostream& operator << (ostream & out, A&a)
{
out << a.m_i;
return out;
}
void main()
{
A a(5);
A b(10);
A c(20);
cout << a << endl;
c = a + b;
cout << c << endl;
cout << (a + b) << endl;
}
运行结果:
5)重载运算符的相关使用规则
从语法上讲,运算符既可以定义为全局函数,也可以定义为成员函数。文献[Murray ,p44-p47]对此问题作了较多的阐述,并总结了表8-4-1的规则。 由于C++语言支持函数重载,才能将运算符当成函数来用,C语言就不行。我们要以平常心来对待运算符重载。 (1)不要过分担心自己不会用,它的本质仍然是程序员们熟悉的函数。 (2〉不要过分热心地使用,如果它不能使代码变得更加易读易写,那就别用,否则会自找麻烦。
不能被重载的运算符
在C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全 方面的考虑,可防止错误和混乱。 (1)不能改变C++内部数据类型(如int, fl oat 等)的运算符。 (2)不能重载‘ . ’,因为 ‘.’ 在类中对任何成员都有意义,已经成为标准用法 (3)不能重载目前C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。 (4)对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。
相关习题: 编写类 String的构造函数、析构函数和赋值函数 已知类String的原型为:
class String
{
public:
String(const char *str = NULL);
String(const String &other);
~String(void);
String & operator =(const String &other);
private:
char *m_data;
};
请编写String的上述4个函数
答案:
String::~String(void)
{
delete[]m_data;
}
String::String(const char *str)
{
if(str == NULL)
{
m_data = new char[1];
*m_data = '\0';
}
else
{
int length = strlrn(str);
m_data = new char[length + 1];
strcpy(m_data,str);
}
}
String & String::operate =(const String &other)
{
if(this == &other)
return *this;
delete[]m_data;
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data,other.m_data);
return *this;
}
|