C++友元类之间的注意点及其主要用法
本次感悟是在C++primer第五版251、252练习的时候得到的,具体的案例大家可以自己查看!
首先友元的用法主要有三种:全局函数做友元、类做友元、最后是成员函数做友元。在这我主要讨论类做友元和成员函数做友元这两个,我感觉全局函数做友元其实是比较简单的,只要将全局函数的声明放到想做友元类的里面就可以。NOTE:友元的声明是不会受到private public protected的影响的,但是尽量统一放到一起。
1.类做友元
类class是C++面向对象语言重要的部分,类可以将数据函数进行封装,但是如果一个类A想访问另一个类B的话怎么办呢啊??也不能说让我看看你的类有啥把,太粗鲁了!我们是文明人,想访问它的成员得成为它的好朋友!!哈哈,让我看看 。那么首先声明一个类叫做屏幕类Screen,你别的别管,就只需知道这个类负责在屏幕上可以显示一些东西就可以,具体的代码不用理解,那首先Sceen上场;
```cpp
class Screen
{
public:
//一些代码
private:
typedef std::string::size_type pos;
pos m_cursor = 0;//光标的位置
pos m_Height, m_Width = 0;//屏幕的高和宽
std::string m_Conents;//屏幕上显示的内容呗,能是啥
///
};
但是我想要个能管理我的窗口类Window,管着我这个Screen,比如最简单的功能就是可以管理指定屏幕Screen的内容消除,行先看看这个Window类把,长什么样子呀!
class Screen;//这里必须提前声明以下,要不下面哪里知道Screen是啥玩意!
class Window
{
public:
//窗口中每个屏幕的编号
using screenIndex = std::vector<Screen>::size_type;
void Clear(screenIndex);
private:
//我有一个vetor,管理一群Screen对象!
std::vector<Screen> m_Screens;
}
那么我想访问Screen就得成为它的朋友:
class Screen
{
friend class Window;//这就完事了!
public:
//一些代码
private:
typedef std::string::size_type pos;
pos m_cursor = 0;//光标的位置
pos m_Height, m_Width = 0;//屏幕的高和宽
std::string m_Conents;//屏幕上显示的内容呗,能是啥
///
};
这样我就可以访问你的类所有私有成员了!比如Window的clear函数:
void Window::Clear(screenIndex i)
{
//创建某块屏幕的引用
Screen&s = m_Screens[i];
//清空某块屏幕的内容
s.m_Conents = (s.m_Width*s.m_Height, ' ');
}
2.成员函数做友元
其实有些Window类的函数是不需要访问Screen类的成员的,没必要,只要特定的函数才需要,那么这时候就是成员函数做友元,只有某个成员函数需要进行访问!
现在假如让clear()函数成为Screen的友元,接下来的步骤:
1.首先你不得有个Window类把,那么定义一个Window类,这时候声明Clear()函数,记住昂不能定义!你肯定问为甚我不能定义它呀??你定义个锤子,你定义它现在能访问Screen吗?不能,先声明它呗,那你肯定会说,我声明完了让他变成它的友元去!好,下一步就是声明它为Screen友元;
2.声明clear()为友元:
class Screen;
class Window
{
public:
//窗口中每个屏幕的编号
using screenIndex = std::vector<Screen>::size_type;
//Here,这就是声明!!!!!
void Clear(screenIndex);
private:
//我有一个vetor,管理一群Screen对象!
std::vector<Screen> m_Screens;
}
class Screen
{
friend void Window::Clear(screenIndex);
public:
//一些代码
private:
typedef std::string::size_type pos;
pos m_cursor = 0;//光标的位置
pos m_Height, m_Width = 0;//屏幕的高和宽
std::string m_Conents;//屏幕上显示的内容呗,能是啥
///
};
成员函数做友元的时候,一定说明它的作用域,要不别人伪装成clear()怎么办!
3.最后一步你就可以对clear()进行定义了:
void Window::Clear(screenIndex i)
{
//创建某块屏幕的引用
Screen&s = m_Screens[i];
//清空某块屏幕的内容
s.m_Conents = (s.m_Width*s.m_Height, ' ');
}
其次我在强调这个友元声明的问题:
建议大家动手试下!!
全部代码放一下:
头文件:
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Screen;//2.因为window需要Screen,所以必须进行声明一下!!
class Window
{
public:
//窗口中每个屏幕的编号
using screenIndex = std::vector<Screen>::size_type;
//1.成员函数做Screen的友元,在这里不能进行定义[friend]
void Clear(screenIndex);
private:
//std::vector<Screen> m_Screens{ Screen(24,80,' ') };
std::vector<Screen> m_Screens;
};//当我们提供一个类内初始值的时候,必须以符号=或者花括号{}表示
class Screen
{
//friend class Window;
using screenIndex = std::vector<Screen>::size_type;
//3友元声明 4.友元定义[可以被定义在这个地方]
friend void Window::Clear(screenIndex);//window::clear(screenIndex);之前必须被声明过了
public:
typedef std::string::size_type pos;
Screen() = default;
Screen(pos ht,pos wd,char c):m_Height(ht),m_Width(wd),m_Conents(ht*wd,c)
{
}
Screen(pos ht, pos wd) :m_Height(ht), m_Width(wd), m_Conents(ht*wd,' ')
{
}
//设置光标处的字符
Screen& Set(char c);
Screen& Set(pos ht ,pos wd,char c);
//返回光标处的字符
const char& Get() const { return m_Conents[m_cursor]; }
//得到指定位置的字符:
const char& GetChar(pos ht,pos wd)const;
char& GetChar(pos ht, pos wd);
//
Screen& move(pos r,pos c);
void SomeMember()const;
//打印screen的内容
const Screen& Display(std::ostream& outPut) const;
Screen& Display(std::ostream& outPut);
private:
void DoDisplay(std::ostream &os)const;
private:
pos m_cursor = 0;//光标的位置
pos m_Height, m_Width = 0;//屏幕的高和宽
std::string m_Conents;
mutable size_t m_AccessCtr;
};
cpp文件:
```cpp
#include "Screenh.h"
//设置当前光标的元素
Screen & Screen::Set(char c)
{
m_Conents[m_cursor] = c;
return (*this);
// TODO: 在此处插入 return 语句
}
//设置指定行列坐标的元素
Screen & Screen::Set(pos ht, pos wd, char c)
{
pos row = (ht-1)*m_Width;
m_Conents[row + (wd-1)] = c;
return (*this);
// TODO: 在此处插入 return 语句
}
//获取指定行列坐标的元素
const char& Screen::GetChar(pos ht, pos wd) const//ht代表高度 wd代表宽度
{
return m_Conents[(ht - 1)*m_Width +(wd-1)];
}
char& Screen::GetChar(pos ht, pos wd)
{
return m_Conents[(ht - 1)*m_Width + (wd - 1)];
}
//更新当前光标的位置
Screen& Screen::move(pos r, pos c)//row:行 col: 列
{
pos row =( r-1)*m_Width;//计算行的位置
m_cursor = row + (c-1);//将行内光标移动到指定的列[都是从00开始呢啊吧]
return *this;
// TODO: 在此处插入 return 语句
}
void Screen::SomeMember() const
{
++m_AccessCtr;
}
const Screen & Screen::Display(std::ostream & outPut) const
{
DoDisplay(outPut);
return *this;
// TODO: 在此处插入 return 语句
}
Screen & Screen::Display(std::ostream & outPut)
{
DoDisplay(outPut);
return *this;
// TODO: 在此处插入 return 语句
}
void Screen::DoDisplay(std::ostream & os) const
{
for (pos i=0;i<m_Height*m_Width;i++)
{
os << m_Conents[i];
if (((i+1)%(m_Width))==0)
{
os << "\n";
}
}
}
/*
*@brief 负责把指定的Screen内容设为空白
*
*@param screenIndex:指定屏幕的编号
*/
void Window::Clear(screenIndex i)
{
//创建某块屏幕的引用
Screen&s = m_Screens[i];
//清空某块屏幕的内容
s.m_Conents = (s.m_Width*s.m_Height, ' ');
}