目录
内联函数
● 任何在类中定义的函数自动地成为内联函数,也可以使用inline 关键字放在类外定义函数前面使之成为内联函数,但是必须使函数体和声明结合在一起, 否则, 编译器只将这个函数视为普通成员函数。 例如:
class Rect
{
int width();
};
inline int Rect::width()
{
}
还可以这样定义内联函数:
class Rect
{
inline int width();
};
int Rect::width()
{
}
● 实现内联成员函数的另一种办法是在类声明的头文件中加入函数的定义。
● 注意: 使用内联函数可以使函数的执行效率提高,但是使用过多的内联函数可能会使代码膨胀,如果将类的析构函数定义为内联函数,可能会导致潜在的代码膨胀。
另外,使用过多的内联函数会占用大量内存空间,使效率降低, 因为对于内联函数来说, 程序会在函数调用的地方直接插入函数代码,所以一般将少数调用频繁的成员函数设置为内联函数,或者说某个成员函数的代码只有一两句 使之成员内联函数, 这样提高程序的执行效率。
友元
● 在C++中, 为了使类的私有成员和保护成员能够被其他类(其中的成员函数)或者其他非成员函数 访问, 引入了友元的概念。
友元提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制
● 如果友元是非成员函数 或者类的成员函数,则成为友元函数; 如果友元是一个类,则成为友元类, 友元类中的所有成员函数都称为另一个类的友元函数。
● 有时需要别的类的成员函数 或者 普通函数 访问某一个类的数据成员, 可以把这些函数定义为该类的友元函数。 友元的作用是提高程序的效率, 但是破坏了类的封装性 和 隐藏性,使得外部函数可以访问某类的私有和保护成员。
友元函数
● 友元函数它不是当前类的成员函数,它不属于任何类,而是定义在类外的普通函数。 友元函数可以是普通函数 或者其他类的成员函数, 是可以直接访问某个类的私有成员的非成员函数。
● 友元函数使用前必须要在类的定义中加以声明,表示该函数可以是友元函数,可以访问该类中的所有数据成员。
声明语法为:
friend 数据类型 标识符();
● 友元函数的声明可以在类定义中的任何位置,没有任何区别。 一个函数可以是多个类的友元函数,只需要在各个类中分别声明。
● 友元函数的调用与一般函数的调用方式和原理一致。
● 我们首先看下普通函数是如何访问类的公有成员数据(不能访问私有和保护成员):
class Rect
{
public:
Rect(int i, int j)
{
width = i;
height = j;
}
int getHeight()
{
return height;
}
int getWidth()
{
return width;
}
private:
int width, height;
};
int rectArea(Rect &myRect)普通函数访问类中的私有数据,
{ 在入口参数传递对象名 或者 对象指针来引用该对象的成员
return myRect.getHeight() * myRect.getWidth();
}
int main()
{
Rect rg(100, 500);
cout << "输出结果为:" << rectArea(rg) << endl;
system("pause");
return 0;
}
● 此时我们只能通过该类的公有属性的成员函数来获得该类的 私有成员数据
● 下面看把普通函数定义友元函数的例子:
class Rect
{
public:
Rect(int i, int j)
{
width = i;
height = j;
}
friend int rectArea(Rect &myRect);
private:
int width, height;
};
int rectArea(Rect &myRect)友元函数访问类中的私有数据
{ 在入口参数传递对象名 或者 对象指针来引用该对象的成员
return myRect.height * myRect.width;
}
int main()
{
Rect rg(100, 500);
cout << "输出结果为:" << rectArea(rg) << endl;
system("pause");
return 0;
}
现在看到我们在 rectArea () 函数的定义中 Rect 的对象可以直接引用其中的数据成员, 这是因为在该类中将此函数声明为友元了。
再看例子:
class Rect
{
public:
Rect(int i, int j)
{
width = i;
height = j;
}
friend void rectArea(void *p); 设置友元函数
private:
int width, height;
};
void rectArea(void *p)友元函数访问类中的私有数据
{ 在入口参数传递对象名 或者 对象指针来引用该对象的成员
Rect *temp = (Rect*)p; 得到对象指针
cout << "输出结果为" << (temp->height*temp->width) << endl;
}
int main()
{
Rect rg(100, 500);
rectArea(&rg);
system("pause");
return 0;
}
在此可以看到使用友元保持了Rect 该类中的数据的私有性, 起到了隐藏数据成员的好处, 又使得特定的类或函数可以直接访问这些隐藏的数据成员。
● 一般来说使用友元函数应该注意的问题:
● 友元函数不是成员函数,类外定义不必加 “ 类名 ::”
● 友元函数不是类的成员,不能直接引用对象成员的名字, 也不能通过this指针引用对象的名字, 必须通过作为入口参数传递进来的对象名 或者 对象指针 来引用该对象的成员。 因此,友元函数一般都带有一个该类的入口参数, 如上列的rectArea(Rect &myRect)
● 当一个非成员函数 或者其他类的成员函数需要访问多个类时, 把这个函数同时定义微这些类的友元函数,这样才能访问这些类的数据。
友元类
● 在开发程序时,如果两个类的耦合度比较紧密, 能够在一个类中访问另一个的私有成员会带来很大的方便。 C++ 语言提供了 友元和友元成员函数 来实现访问其他类的私有成员。
当开发者希望另一个类能够访问当前类的私有成员时, 可以在当前类的定义中将另一个类声明为自己的友元类, 这样在另一个类中就可以访问当前类的私有成员了。
● 友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的所有成员。
下面看一个另一个类作为当前类的友元的例子:
class Cltem
{
private:
char name[128];
void outputName()
{
cout << name << endl;
}
public:
friend class CList; 将CList类作为自己的友元类
void setName(const char *datas)
{
if (datas != nullptr)
{
strcpy_s(name, datas);
}
}
};
class CList
{
private:
Cltem myCltem;
public:
void outputltem()
{
myCltem.setName("huang"); 调用Cltem 类的公有函数
myCltem.outputName(); 调用Cltem 类的私有函数
}
};
int main()
{
CList myCList;
myCList.outputltem();
system("pause");
return 0;
}
使用友元类应该注意的问题:
● 友元类不能被继承, 也就是说,CList 的子类 不是 Cltem 的友元类
● 友元关系是单向的, 不具有交换性; 若类 CList 是Cltem 的友元类, Cltem 不一定是 CList 的友元类,这个是由类是否有相应的声明决定。
● 友元关系不具有传递性,若类 CList 是Cltem 的友元类,类Temp 是CList 类的友元类,类Temp 不一定是Cltem 的友元类,这个是由类是否有相应的声明决定。
友元成员函数
● 在开发程序时, 有时需要另一个类对当前类的私有成员的方法。 例如, 假设需要实现只允许 CList 类的某个成员函数 访问 Cltem 类的私有成员, 而不允许其他CList类中的成员函数访问 Cltem 类的私有数据, 便可以通过定义友元函数来实现。
在定义 Cltem 类时, 可以将CList 类的某个方法定义为友元方法, 这样就限制了只有该方法允许访问 CItem 类的私有成员。
● 如果一个类的成员函数是另一个类的友元函数, 通过友元成员函数不仅可以访问自己所在类对象中的所有其他成员, 还可以由关键字friend 声明语句所在的类对象中的所有成员。
下面看一个代码示例:
class Cltem; 前导声明 Cltem 类
class CList
{
private:
Cltem *myCltem;
public:
CList();
~CList();
void outputltem();
};
class Cltem
{
private:
friend void CList::outputltem(); 定义outputltem 为 Cltem 类的友元成员函数
char name[128];
void outputName()
{
cout << name << endl;
}
public:
void setName(const char *datas)
{
if (datas != nullptr)
{
strcpy_s(name, datas);
}
}
};
void CList::outputltem()
{
myCltem->setName("huang");
myCltem->outputName(); 访问了 CItem 类的私有方法
}
CList::CList()
{
myCltem = new Cltem;
}
CList::~CList()
{
delete myCltem;
myCltem = nullptr;
}
int main()
{
CList myCList;
myCList.outputltem();
system("pause");
return 0;
}
注意: 当一个类的成员函数作为另一个类的友元函数时, 必须先定义成员函数所在的类, 如上述代码类CList 的成员函数 outputltem() 为类 Cltem 的友元函数, 就必须先定义类CList; 并且在声明友元函数时, 要加上这个成员函数所在类的类名和作用域运算符 “ :: " , 如上例语句:
friend void CList::outputltem(); 定义友元成员函数
另外,在主函数中一定要创建类 CList 的一个实例对象, 只有这样才能通过对象名调用友元函数。
最后,如果在类定义前要使用该类的成员, 需要在使用前对该类进行声明, 如上述声明语句
class Cltem; 前导声明 Cltem 类
否则系统会报错。
下面再看一个例子:
class Cltem
{
friend void outPut(Cltem *myCltem); 声明outPut为友元函数
private:
char name[128];
void outputName()
{
cout << name << endl;
}
public:
void setName(const char *datas)
{
if (datas != nullptr)
{
strcpy_s(name, datas);
}
}
};
void outPut(Cltem *myCltem)
{
if (myCltem != nullptr)
{
myCltem->setName("huang"); 调用Cltem 的公有方法
myCltem->outputName(); 调用Cltem 的私有方法
}
}
int main()
{
Cltem myCltem;
outPut(&myCltem); 通过友元函数访问Cltem 类的私有方法
system("pause");
return 0;
}
const 成员函数
● 使用 const 成员函数 要注意的问题有:
● 常成员函数不能更改对象中数据成员的值, 也不能调用非类常成员函数。
● 如果将该类实例化的对象声明为 常对象, 那么这个对象只能调用常成员函数。
● 对于常成员函数,后面的const 关键字 可以作为重载函数的区分。
● 要同时在成员函数的声明和定义中指明const 限定符
● 如果必须在const 成员函数中修改某些数据的值,正确的方法是:声明这些数据成员时添加前辍 mutable 限定符,这样即使在const 成员函数中也能修改成员数据的值。
● const 成员函数 不能在它的实现中调用另一个 非const 成员函数,因为相同的对象(相同的this 指针)也可以调用非const 成员函数,而非const 成员函数可以随意修改对象。
class Line
{
public:
Line(int x1, int x2, int y1, int y2);
void setpoint1(int x, int y);
void setpoint2(int x, int y);
void Draw()const;
void Draw();
private:
int m_x1, m_x2, m_y1, m_y2;
};
Line::Line(int x1, int x2, int y1, int y2)
{
m_x1 = x1;
m_x2 = x2;
m_y1 = y1;
m_y2 = y2;
}
void Line::setpoint1(int x, int y)
{
m_x1 = x;
m_y1 = y;
}
void Line::setpoint2(int x, int y)
{
m_x2 = x;
m_y2 = y;
}
void Line::Draw()const
{
cout << "point1(" << m_x1 << "," << m_y1 << ")" << \
" " << "point2(" << m_x2 << "," << m_y2 << ")" << endl;
}
void Line::Draw()
{
cout << "point1(" << m_x1 << "," << m_y1 << ")" << \
" " << "point2(" << m_x2 << "," << m_y2 << ")" << endl;
}
int main()
{
const Line In(0, 20, 200, 100); 创建常对象
In.setpoint1(0, 0); 错误, 因为企图更改对象中成员变量的值
In.Draw(); 正确,调用const 成员函数
Line In2(0, 20, 200, 100);
In2.Draw(); 调用非常成员函数 Draw()
system("pause");
return 0;
}
编译器如何实现 const 成员函数
● 编译器如何检测到成员函数为为数据成员赋值?
这非常简单, 数据成员和成员函数之间唯一的连接就是this指针。 const成员函数必须把调用它的对象当做const 对象, 这也可以通过将this 指针声明为 指向const 的指针轻松做到, 例如:
unsigned HowMany(const TIntStack *this);
根据这个声明, 任何通过指针给对象内部的数据成员赋值都是非法的,因为该this指针是一个指向常量的指针。