类和对象(下)(二)

简介: 类和对象(下)(二)

友元

friend关键字修饰函数,使得这个函数是可以突破类的封装,但是友元会增加耦合度(关联程度),破环了封装,所以友元不能多用

分为:友元函数和友元类

友元函数就是在一个类中对于成员函数加上friend修饰,在类外进行定义

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

因为在类中成员函数会使得this指针占据第一个位置,对于某些功能如重载<<需要cin在第一个参数位置(与this就会争夺,但是this默认为第一个位置的形参),所以我们考虑到可以不使用成员函数,将重载<<放在全局域,但是这个时候因为private限制导致类外无法访问自定义类型,所以我们推出来友元这个定义,实际上就是一个普通函数,只是在类中声明的普通函数(没有this指针),但是因为在类中所以可以访问私有成员变量

注意:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

友元类

友元类实际上就是在另一个类中声明的一个类,使得这个友元类的所有(成员变量or成员函数)都可以访问该类的私有成员

//下面是对于友元类的例子
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class A {
public:
  int get() {
    return d._year;
  }
private:
  Date d;//创建Date类中的对象d ,然后就可以通过d来调用Date的私有成员变量了    
};
class Date
{
  friend class A;//声明A为Date的友元类
public:
  Date(int year, int month, int day)
    :_year(year)
    , _month(month)
    , _day(day)
  {}
  //重载>>运算符
   friend ostream& operator>>(ostream& out, Date& d);
  //{
  //  
  //}
private:
  int _year;
  int _month;
  int _day;
};
ostream& operator>>(ostream& out, Date& d)
{
  out << d._year << "-" << d._month << "-" << d._day << endl;
}
int main()
{
  return 0;
}

注意:

  • 友元关系的单向的,不具有交换性,如上述Date不能访问A的私有成员,但是A可以访问Date的私有成员
  • 友元关系是不能传递的
  • 友元关系不能继承,继承的话属于父子类,这个概念后序再讲

实际上友元类,在其中声明Date类成员,就是因为当我们需要使用这个Date类中的成员变量时候,通过friend访问访问私有or保护的成员变量,从而实现类外访问

内部类

内部类:如果一个类定义在另一个类的内部,就叫做内部类,内部类是独立的个体,不属于外部类,也并不能通过外部类的对象去访问内部类的成员,但是内部类是天生的友元类(外部类的友元)

特征:

内部类可以放在外部类的任何地方,不受访问限制符的限制

内部类可以直接访问外部类static成员,不需要外部类的类名or对象

sizeof(外部类)=外部类,内部类不影响外部类的大小

//例子
class A
{
  friend class C;
public:
  void get()
  {}
  class B//内部类  
  {
  public:
    void foo(const A& a)
    {
      cout << k << endl;//OK  //可以直接访问静态变量不需要类名or对象
      cout << a.h << endl;//OK
    }
  };
private:
  int h;
  static int k;
};
class C//友元类
{
public:
  void get(C& c)
  {
    a.h;
    a.k;
  }
  int date;
  A a;
};
int A::k = 0;
int main()
{
  return 0;
}

匿名对象

匿名对象的使用对于某些场景的情况下是比较好用的,匿名对象就是直接用类名加上(),如:A(),来使用的,创建这个对象之后,再下一行代码就会析构,所以匿名对象由此得名(不会留下痕迹)

//匿名对象的用法
class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a)" << endl;
    }
    A(const A& aa)
        :_a(aa._a)
    {
        cout << "A(const A& aa)" << endl;
    }
    A& operator=(const A& aa)
    {
        cout << "A& operator=(const A& aa)" << endl;
        if (this != &aa)
        {
            _a = aa._a;
        }
        return *this;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};
int main()
{
  //正常实例化对象
    A a(1);//1调用构造函数,实例化对象a
    //当只有无参构造的时候(or默认构造函数) 实例化对象应该为 A a;//这样即可  不能加上()
    //A a();  这个代码是错误的,因为编译器无法判断你这个是函数的声明还是实例化对象
    //匿名对象的创建
    A(2);//这个就是匿名对象,我们可以发现,在编译器在运行到下一行的时候就会调用析构函数,销毁这个匿名对象
    //匿名对象的适合使用的场景,比如在,使用stl的时候,放进去vector容器,使用匿名对象,后序再说
    return 0;    
}

拷贝对象的编译器的优化

前文,我们讲到了,对于同一行的代码连续调用构造函数(or拷贝构造函数),按照我们的理论会正常进行调用这两个函数,但是编译器对于这个做法,进行了优化,使得无论理论上多少次调用,但是经过优化,只需要调用一次构造函数

class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a)" << endl;
    }
    A(const A& aa)
        :_a(aa._a)
    {
        cout << "A(const A& aa)" << endl;
    }
    A& operator=(const A& aa)
    {
        cout << "A& operator=(const A& aa)" << endl;
        if (this != &aa)
        {
            _a = aa._a;
        }
        return *this;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};//我们继续使用这个类的模板
void f1(A aa)
{}
A f2()
{
  A aa;
  return aa;
}
int main()
{
    //非连续使用构造函数
    A a1(1);
    A a2=a1;//我们这样是分开的,不会进行优化
    //连续调用
    f1(A(2));//这样的话,理论上我们就会先进行匿名构造,然后传值拷贝构造创建临时对象赋值给aa,所以是连续两次构造,编译器进行优化为1次
    //当返回值为自定义类型A 的时候+赋值
    A a3=f2();//先f2的返回值需要调用拷贝构造,返回一个临时对象,然后需要赋值给a3(再次调用拷贝构造)   所以 (aa返回)拷贝构造 + (a3赋值)拷贝构造 = 优化为一次构造(一步到位,临时对象直接给a3)
    return 0;
}

首先我们要认识到赋值是什么,拷贝构造是什么,什么时候是赋值什么时候是拷贝构造

我们知道一点即可,是否已经创建了这个对象

如果早就创建了对象,那么就是赋值

如果没有,那么就是拷贝构造

new创建对象

对于c++、java这样的编程语言的话,那么我们一定知道new一个对象这样的说法,接下来先了解一下new

在C语言中我们去动态申请空间的时候,使用到malloc、calloc等函数,但是过程比较繁琐,需要强制类型转换等,对于C++来讲,我们也能动态申请空间,使用new关键字来实现

#include<malloc.h>
int main()
{
    //创建一个数组,动态申请空间
    int* a1=(int*)malloc(sizeof(int)*10);//这样是C语言的方式
    free(a1);//释放空间
    //c++
    int* a2=new int[10];//这样就可以啦
    delete[] a2;//delete关键字来释放空间,要根据类型,这个是new [] 所以释放空间的时候跟着[]
  //new也可以做到初始化
    int* a3=new int(10);//初始化数值为10,但是不能做到calloc那样,对于数组来进行全部的赋值(统一值)
    //int* a4=(int*)calloc(0,sizeof(int)*10)//a4[]数组初始化值都为0
    int* a5=new int[5]{1,2,3};//这样对于数组进行初始化是可以的(使用大括号)
    //前三位为1 2 3 后两位为0
    return 0;
}

实际上可以实现数组元素全部为0,如int* pa=new int[10]{};//这样就可以实现了类似于calloc(0,sizeof(int)*10);一样的效果

当然除了内置类型可以new,自定义类型也可以,但是这两个都需要用对应类型的指针的方式来接收,因为new是申请的空间(堆空间),返回的也是地址

//演示样例
class A
{
public:
    A(int a, int b)
        :_a(a)
        , _b(b)
    {}
    void Print()
    {
        cout << _a << " " << _b << endl;
    }
private:
    int _a, _b;
};
int main()
{
    A* a1 = new A(1, 2);//开空间+调用构造函数初始化  这个也进行了构造函数优化
    a1->Print();
    return 0;
}


相关文章
|
数据采集 监控 算法
阿里云百炼模型训练评测
【7月更文挑战第1天】阿里云百炼提供一站式的模型开发服务,包括大模型训练、调用与部署。用户可查看剩余调用次数,点击开通服务以使用模型。计费基于调用量,涵盖推理、训练和部署。开通服务需同意协议,成功后将收到短信通知。评测显示,平台功能丰富,易用性强,能显著提升模型效果,且模型部署简便。建议优化数据预处理工具并增加实例教程。
1094 5
|
JavaScript 数据安全/隐私保护
SAP PP使用ECR去修改BOM主数据(一)
SAP PP使用ECR去修改BOM主数据(一)
SAP PP使用ECR去修改BOM主数据(一)
|
C++ iOS开发
IOS 的代码规范
正文: • 格式化代码 ◦ 指针“*”号的位置 ▪ 如:NSString *varName; ◦ 空格 VS tabs ▪ 只允许使用空格,将编辑器设置为1个TAB = 2个字符缩进 ◦ 每行的长度 ▪ 每行最多不得超过100个字符 ▪ 以15寸Macboo...
1102 0
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
33078 81
如何保证分布式文件系统的数据一致性
|
前端开发 容器
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局(上)
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局
17818 24
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36801 22
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24873 15
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36786 15
重生之---我测阿里云U1实例(通用算力型)

热门文章

最新文章