title: 友元/内联函数
date: 2020-09-01 19:36:01
tags: c++
categories: c++
1、友元
在程序里,类中有些私有属性(private)除了本类的成员访问它,也想让类外特殊的一些函数或者类进行访问,这就需要用到友元的技术。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
1.1 友元函数
在当前类外定义的函数,但是当前类中声明,且函数名前加frend关键字,这样的函数就是友元函数。友元函数可以是不属于任何类的全局函数,也可以是其它类中的成员函数。
友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。
1.1.1 全局函数做友元函数
//全局函数做友元
#include <iostream>
#include <string>
using namespace std;
class Persion
{
public:
int m_age;
string GetPassword(void);
string GetIDNumber(void);
Persion(int age,string psw,string idnumber);
~Persion(void);
//声明两个友元函数
friend void SetPassword(string psw,Persion &p);
friend void SetIDNumber(string id,Persion &p);
private:
string Password;
protected:
string ID_Number;
};
Persion::Persion(int age,string psw,string idnumber):m_age(age),Password(psw),ID_Number(idnumber)
{
}
Persion::~Persion(void)
{
}
string Persion::GetPassword(void)
{
return Password;
}
string Persion::GetIDNumber(void)
{
return ID_Number;
}
/**************************************
* 两个全局友元函数的实现,虽然他它们在类中进行了声明
* 但是它们并不是类的成员函数
* **********************************/
void SetPassword(string psw,Persion &p)
{
p.Password = psw; //友元函数成功的访问到了类中的私有成员
}
void SetIDNumber(string id,Persion &p)
{
p.ID_Number = id; //友元函数成功的访问到了类中的保护成员
}
void test_01(Persion &p)
{
/****************************************************
* 直接在类外访问私有属性Password和保护属性ID_Number会报错,因为这两个成员类外不能访问
****************************************************/
// cout<<p.Password<<endl;
// cout<<p.ID_Number<<endl;
/******************************************************
* 在类外使用类中的成员函数可以访问到私有属性和受保护的属性
******************************************************/
cout<<p.GetIDNumber()<<endl;
cout<<p.GetPassword()<<endl;
/******************************************************
* 使用全局友元函数对类中的私有属性和保护属性进行更改
******************************************************/
SetPassword("6543210",p);
SetIDNumber("201515",p);
cout<<p.GetIDNumber()<<endl;
cout<<p.GetPassword()<<endl;
}
int main()
{
Persion p1(18,"0123456","152015");
test_01(p1);
return 0;
}
SetPassword()和SetIDNumber()是两个全局范围内的非成员函数,它们不属于任何类,它们的作用分别是对Password和ID_Number两个值进行修改。但是Password和ID_Number两个值是类Persion中的私有成员和保护成员,原则上不能访问。但是这两个函数又要使用这两个值,所以将这两个函数声明为Persion类的友元函数。
1.1.2 其它类的成员函数做友元函数
#include <iostream>
#include <string>
using namespace std;
class Address; //类的声明,在Student中ShowInfo()方法用到了Address类,该类的实现在后面。因此需要声明
class Student
{
public:
string m_name;
Student(string name,int age,char sex):m_name(name),m_age(age),m_sex(sex){}
void ShowInfo(Address &addr);
protected:
int m_age;
char m_sex;
};
class Address
{
public:
Address(string provice,string city,string districy):m_province(provice),m_city(city),m_district(districy){}
/***************************************************************
* 声明Student类中的ShowInfo方法为友元函数,可以访问本类中的所有属性
****************************************************************/
friend void Student::ShowInfo(Address &addr);
private:
string m_province; //省份
string m_city; //城市
string m_district; //区(市区)
};
void Student::ShowInfo(Address &addr)
{
cout<<this->m_name<<"的性别是:"<<this->m_sex<<",年龄是:"<<this->m_age<<",地址:"<<
addr.m_province<<addr.m_city<<addr.m_district<<endl;
};
int main(int argc, const char** argv) {
Address XMAddr("浙江省","杭州市","萧山区");
Student std("小明",18,'M');
std.ShowInfo(XMAddr);
return 0;
}
在本例中ShowInfo()函数就是Student类中的成员函数用来做友元函数。Address
类中有三个私有属性,只能由本类的对象或者成员函数访问,而ShowInfo
方法是Student
类的成员函数,它要想访问Address
类中的私有属性就可以在Address
类中声明函数ShowInfo
为友元函数,即可访问。
1.12友元类
不仅函数可以做友元,还可以将整个类声明为另一个类的"朋友",这就是友元类。友元类中的所有成员函数都是另外一个类的友元函数。
例如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。
#include <iostream>
#include <string>
using namespace std;
class A
{
friend class B; //申明B类为A类的友元类,B中的所有成员属性可以访问A类中的所有成员和属性
private:
int a1;
protected:
int a2;
void ShouAInfo()
{
cout<<"a1 = "<<a1<<",a2 = "<<a2<<endl;
}
int AddASUM(void)
{
return a1+a2;
}
public:
A(int _a1, int _a2):a1(_a1),a2(_a2){}
};
class B
{
private:
int b1;
int b2;
public:
B(int _b1, int _b2):b1(_b1),b2(_b2){}
/*****************************************************
*B类为A类的友元,B类中的任何成员都可访问A类的任何成员和属性
*******************************************************/
void ShowA_A1(A &a)
{
cout<<a.a1<<endl;
}
void SetB1(A &a)
{
b1 = a.AddASUM();
}
};
int main(int argc, char const *argv[])
{
A a(1,2);
B b(3,4);
b.ShowA_A1(a);
b.SetB1(a);
return 0;
}
友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。
除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。
2、内联
c++内联函数通常和类一起 使用,如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。如果要把一个函数定义为内联函数,需在前面加上关键词inline
,如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。这与define有点相似。对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
#include <iostream>
using namespace std;
#define MIN(a,b) {a>b?b:a} //宏替换,在预处理阶段
inline int MAX(int a, int b) //内联处理由编译器完成
{
return a > b ? a : b;
}
int main(int argc, const char** argv) {
int min = MIN(7, 6);
cout << MAX(8, 9) << endl;
cout << min;
return 0;
}
内联函数的目的是为了解决程序中函数调用的效率问题。程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的节省。所以内联函数一般都是1-10行的小函数。在使用内联函数时要留神:
-
1.在内联函数内不允许使用循环语句和开关语句;
-
2.内联函数的定义必须出现在内联函数第一次调用之前;
-
3.类结构中所在的类说明内部定义的函数是内联函数;
inline和define的区别5
内联和宏定义都看起来是替换,但还是有区别的。
- 宏定义是在预处理阶段由预处理器进行替换,内联是在编译阶段由编译器进行替换处理的。
- 编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
- 内联函数可以访问类的成员变量,宏定义则不能;
- 在类中声明同时定义的成员函数,自动转化为内联函数。