初识C++ - 类与对象(下篇·下)

news2024/11/18 23:39:53

目录

再谈构造函数

隐式类型的转换

explicit关键字

单参数

多参数

static静态

一道关于static的题目

友元

友元函数

友元类

内部类

匿名对象

拷贝对象时的一些编译器优化

结束语


再谈构造函数

1.1 构造函数体赋值
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
class Date
{
public:
Date(int year, int month, int day)
 {
     _year = year;
     _month = month;
     _day = day;
 }
private:
    int _year;
    int _month;
    int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量
的初始化, 构造函数体中的语句只能将其称为赋初值 ,而不能称作初始化。因为 初始化只能初始
化一次,而构造函数体内可以多次赋值
1.2 初始化列表
初始化列表:以一个 冒号开始 ,接着是一个以 逗号分隔的数据成员列表 ,每个 " 成员变量 " 后面跟
一个 放在括号中的初始值或表达式。
class Date
{
public:
Date(int year, int month, int day)
     : _year(year)
     , _month(month)
     , _day(day)
 {}

private:
int _year;
int _month;
int _day;
};

 使用

【注意】
1. 每个成员变量在初始化列表中只能出现一次( 初始化只能初始化一次 )
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
        ①引用成员变量
        ②const成员变量
        ③自定义类型成员(且该类没有默认构造函数时)
class A
{
public:
     A(int a)
     :_a(a)
     {}
private:
 int _a;
};

class B
{
public:
     B(int a, int ref)
     :_aobj(a)
     ,_ref(ref)
     ,_n(10)
     {}
private:
     A _aobj;  // 没有默认构造函数
     int& _ref;  // 引用
     const int _n; // const 
};

3. 尽量使用初始化列表初始化,因为 不管你是否使用初始化列表(即使没有也会走一遍) ,对于自定义类型成员变量,一定会先使用初始化列表初始化。
4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序, 与其在初始化列表中的先后次序无关

一道题目

class A
{
public :
    A ( int a )
      : _a1 ( a )
      , _a2 ( _a1 )
  {}
   
    void Print () {
        cout << _a1 << " " << _a2 << endl ;
  }
private :
    int _a2 ;
    int _a1 ;
};
int main () {
    A aa ( 1 );
    aa . Print ();
}
/*
A . 输出 1   1
B . 程序崩溃
C . 编译不通过
D . 输出 1   随机值
*/

隐式类型的转换

explicit关键字

        构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用 explicit 修饰构造函数,将会禁止构造函数的隐式转换

单参数

//隐式类型的转换
class Date
{
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
	// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译

	Date(int year)    //当在此语句前添加explicit就不会进行隐式类型的转换了
		:_year(year)
	{}


	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022);
	//隐式类型的转换
	Date d2 = 2022;	//先构造,再拷贝构造,但是现在的编译器可以直接构造

	Date d3(d1);
	Date d4 = d1;

	const Date& d5 = 2022;	//引用

	return 0;
}

检测优化的方式 

//统计A对象创建了多少个,这里测试的比较少,还有传值传参、传值返回之类的都会调用构造
int N = 0;
class A
{
public:
	A(int a = 0)	//构造
		:_a(a)
	{	
		N++;
	}

	A(const A& aa)	//拷贝构造 -- 拷贝构造也算构造
		:_a(aa._a)
	{
		N++;
	}
private:
	int _a;
};


int main()
{
	A aa1(1);
	A aa2 = 1;	//显然这里是被优化之后的结果,否则会显示4个
	A aa3 = aa1;

	cout << N << endl;

	return 0;
}

用途

多参数

//隐式类型的转化
class Date
{
public:


// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用
// explicit修饰构造函数,禁止类型转换
Date(int year, int month = 1, int day = 1)	//缺省一个,或者全缺省也可以发生隐式类型的转换
	: _year(year)
	, _month(month)
	, _day(day)
	{}

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

//注意是C++11后面支持的多参数,单参数的隐式类型转换在C++98就已经支持了
int main()
{
	//隐式类型的转换
	Date d1 = (2022,12,22);	//先构造,再拷贝构造,但是现在的编译器可以直接构造
	//等价于下面的写法,但是本质上是不同的,上面是优化后的结果
	Date d2(2022, 12, 22);

	const Date& d3 = { 2022 ,12 ,22};	//引用

	return 0;
}

static静态

优化下检测,引入静态成员函数与静态成员变量 

//static -- 静态成员变量与静态成员函数
//显然我们通常并不会去使用全局变量,容易被误改
//将它约束到类中显然是一种好方法
class A
{
public:
	A(int a = 0)	//构造
		:_a(a)
	{
		N++;
	}

	A(const A& aa)	//拷贝构造 -- 拷贝构造也算构造
		:_a(aa._a)
	{
		N++;
	}

	static int GetN()//为了让外部得到内部的静态数据
                     //可以利用静态成员函数来实现,通常这俩是一起的
	{                //当然这里是不能访问非静态的成员,静态的只能访问静态的
		return N;
	}

private:
	int _a;

	static int N;	//这是声明,所以不能定义初始化,也不能通过初始化列表去初始化
					//注意这个N并不在栈帧中,而是在静态区中
					//我们也一般设置成私有的,而不是共有的
};
int A::N = 0;	//定义初始化,只能在类外部去初始化,注意要指定类域


int main()
{
	A aa1(1);
	A aa2 = 1;	

	cout << aa1.GetN() << endl;

	cout << A::GetN() << endl;	//有了静态成员函数也可以直接调用,不需要重新定义类对象出来

	A* ptr = nullptr;			//这种也是可以的,不过我们一般不这样做
	cout << ptr->GetN() << endl;

	return 0;
}

一道关于static的题目

JZ64 求1+2+3+...+n -- 链接

描述
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0 < n \le 2000<n≤200

示例1
输入:
5

返回值:
15

示例2
输入:
1

返回值:
1

进阶: 空间复杂度 O(1) ,时间复杂度 O(n)

解答

class sum{
public:
    static int GetRet() //让外部得到数据
    {
        return _ret;
    }

    sum()
    {
        _ret += _i;
        _i++;
    }
private:
    static int _i;  //静态的,让其共享一份
    static int _ret;
};
int sum::_i = 1;
int sum::_ret = 0;


class Solution {
public:
    int Sum_Solution(int n) {
        sum a[n];
        return sum::GetRet();
    }
};

//要求禁止常规创建对象的解决方法

class A{
public:
    static A GetObj(int a = 0)  
    {
        A aa1(a);
        return aa1;
    }

private:        //放到私有就无法常规创建对象了
    A(int a = 0)
        :_a(a)
    {      
    }
private:
    int _a;
};


int main()
{
    //static A aa1;       //拷贝构造
    //A* ptr = new aa3;   //new 在堆上创建
    //A aa2;              //最常见的构造       这三种都不可以创建了

    A aa = A::GetObj(10);   //静态成员函数没有this指针

    return 0;
}

友元

        友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
        友元分为: 友元函数和友元类

友元函数

问题:现在尝试去重载 operator<< ,然后发现没办法将 operator<< 重载成成员函数。 因为 cout 输出流对象和隐含的 this 指针在抢占第一个参数的位置 this 指针默认是第一个参数也就是左操作数了。但是实际使用中cout 需要是第一个形参对象,才能正常使用。所以要将 operator<< 重载成 全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>> 同理。

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

class Date
{
    friend ostream& operator<<(ostream& _cout, const Date& d);
    friend istream& operator>>(istream& _cin, Date& d);
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
private:
    int _year;
    int _month;
    int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
    _cout << d._year << "-" << d._month << "-" << d._day;
    return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
    _cin >> d._year;
    _cin >> d._month;
    _cin >> d._day;
    return _cin;
}
int main()
{
    Date d;
    cin >> d;
    cout << d << endl;
    return 0;
}
说明 :
        1、友元函数可访问类的私有和保护成员,但 不是类的成员函数
        2、友元函数不能用 const 修饰
        3、友元函数可以在类定义的任何地方声明, 不受类访问限定符限制
        4、一个函数可以是多个类的友元函数
        5、友元函数的调用与普通函数的调用原理相同

友元类

        友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
①友元关系是单向的,不具有交换性。 A是B的友元,但是反过来B却不是A的友
比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接
访问 Time 类的私有成员变量,但想在 Time 类中访问 Date 类中私有的成员变量则不行。
②友元关系不能传递
如果C B 的友元, B A 的友元,则不能说明 C A 的友元。
友元关系不能继承,在继承再做介绍。

内部类

概念: 如果一个类定义在另一个类的内部,这个内部类就叫做内部类 。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意: 内部类就是外部类的友元类 ,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。

优化上面所做的题目

class Solution {
  public:
    class sum {     //内部类
      public:
        sum() {
            _ret += _i;
            _i++;
        }

    };

    int Sum_Solution(int n) {
        sum a[n];
        return _ret;
    }
  private:
    static int _i;  //静态的,让其共享一份
    static int _ret;
};
int Solution::_i = 1;
int Solution::_ret = 0;

这样看起来要简洁许多了

注意事项:

        关于友元与内部类其实C++并不太喜欢用,Java用的比较多,这种行为会破环类的封装

 

匿名对象

//匿名对象
class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a)" << endl;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};

class Solution {
public:
    int Sum_Solution(int n) {
        //...
        return n;
    }
};

A F()
{
    //A aa1(10);
    //return aa1;
    return A(10);   //有了匿名对象上面两句就直接可以简化成一句了
}
int main()
{
    A aa1;
    // 不能像下面这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
    //A aa1();
     
    // 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
    // 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数

    A();
    A aa2(2);

    // 匿名对象在这样场景下就很好用,只调用类里面的一个函数,当然还有一些其他使用场景
    Solution().Sum_Solution(10);

    return 0;
}

拷贝对象时的一些编译器优化

        在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

 

//拷贝对象时的一些编译器优化

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;
}

A f3()
{
    //A aa(10);
    //return aa;

    return A(10);
}
int main()
{   
    //优化场景1
    //A aa1 = 1;  //A tmp(1) + A aa1(tmp) -> 优化 A aa1(1)


    //优化场景2
    //A aa1(10);
    //f1(aa1);

    //f1(A(10));  // 构造 + 拷贝构造 -> 优化 构造
    //f1(1);  //有隐式类型转换 构造 + 拷贝构造 -> 优化 构造


    //优化场景3
    //f2();
    //A ret = f2();   //构造+拷贝构造+拷贝构造 -> 优化 构造

    //不可取
    //A ret;
    //ret = f2();


    //优化场景4
    A ret = f3();

 
    return 0;
}

 

 

结束语

钟鼎山林都是梦,人间荣辱休惊,只消闲处过平生。
                                                                -- 《临江仙·再用前韵,送祐之弟归浮梁》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服装进销存管理软件哪个比较好用?

做好库存是服装行业是保障店铺正常运营重要方面。如果只是靠人工清点记录服装库存情况、手工记账&#xff0c;会花费大量的人员和精力&#xff0c;还不能保证一定的效率和准确率。而且服装业具有鲜明的行业特性&#xff1a;服装款式多、季节性强、颜色/尺码等等&#xff0c;如果…

Influxdb双写服务influxdb-relay部署配置【离线】

Background Influxdb社区版未提供集群方案&#xff0c;官方提供的集群模式为闭源收费版本&#xff0c;具体收费明细不太清楚哈&#xff0c;有知道的请留言告知哈。官方开源的influxdb-relay仅仅支持双写功能&#xff0c;并未支持负载均衡能力&#xff0c;仅仅解决了数据备份的问…

【C++初阶】友元(友元函数友元类)、内部类、匿名对象、拷贝对象时的优化

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【C学习与应用】 ✒️✒️本篇内容&#xff1a;友元函数和友元类的概念和基础应用&#xff0c;简单介绍内部类、匿名对象、拷贝对象时的部分编译器优化情况…

Java守护线程简述

Java守护线程简述前言前置知识线程JVM退出代码测试查看子线程是否继承父线程的类型守护线程在程序退出时的表现普通线程在程序退出时的表现总结前言 最近再看《Java并发编程实战》&#xff0c;正好有一小节关于守护线程的知识&#xff0c;这里做一点小总结。 前置知识 这里只…

云原生之Dockerfile简介和基础实践

dockerfile简介和基础实践一、Dockerfile简介1.1、Dockerfile解决的问题1.2、docker build 构建流程1.3、关键字介绍二、Dockerfile 实践2.1、基本语法实践 --- golang问题检查2.2、基本语法实践 --- gcc总结后言一、Dockerfile简介 Dockerfile是一个创建镜像所有命令的文本文…

为行业赋能 助力行业客户业务大放异彩

近日&#xff0c;2022亚马逊云科技re:Invent全球大会已完美落幕&#xff0c;在大会上发布了很多重磅新品&#xff0c;包括云原生数据战略、硬件创新、高性能计算等等在各行各业中的创新应用&#xff0c;下面就来看看医疗与生命科学、市场调研和数据分析、汽车行业&#xff0c;他…

如何理解UML2.5.1(04篇)

第一步&#xff1a; 这里发现UML2.5.1中的一处错误&#xff1a; 图四、Figure9.10中的一处错误。 错误就在于最下面一个关联右端点处的标记redefines&#xff0c;有了这个标记&#xff0c;就应该意味着此关联特化了某个关联&#xff0c;但是如果我们用“A_ownedAttribute_class…

在Android端集成OpenCV的三种方式

1.Opencv Android SDK 基于Opencv C本地代码&#xff0c;通过Java语言接口使用JNI技术调用C本地方法的SDK开发包。 &#xff08;1&#xff09;etc:各类模型文件存储地址 &#xff08;2&#xff09;java:Java版本的Android SDK相关文件 &#xff08;3&#xff09;native:JNI层…

【QGIS入门实战精品教程】3.4:QGIS创建GeoPackage地理数据库及数据入库案例详解

GeoPackage(以下简称gpkg),内部使用SQLite实现的一种单文件、与操作系统无关的地理数据库。在QGIS中可以很方便的实现GeoPackage的创建与连接等操作。 一、QGIS创建GeoPackage 1. 创建数据库 QGIS创建GeoPackage的方法与ArcGIS中创建File GDB的类似,选择一个目标文件夹,…

Adobe Acrobat XI 一进去就闪退;解决Acrobat的闪退问题

一、原因分析 闪退是因为网络中校验版权时出现问题 二、解决办法 域名欺骗&#xff0c;添加伪造的host条目 1. 进入C:\Windows\System32\drivers\etc 找到hosts文件 2. 右机hosts&#xff0c;选择属性&#xff0c;在安全选项里&#xff0c;点击高级&#xff1a; 3. 在ho…

Hive+Spark离线数仓工业项目实战--数仓设计及数据采集(1)

数仓设计及数据采集 1. **数据仓库设计** - 建模&#xff1a;维度建模&#xff1a;【事实表、维度表】 - 分层&#xff1a;ODS、DW【DWD、DWM、DWS】、APP - **掌握本次项目中数仓的分层** - ODS、DWD、DWB、DWS、ST、DM 2. 业务系统流程和数据来源 - 数据源…

Webpack5搭建Vue环境 | Webpack

文章目录webpack打包其他资源图片资源file-loader文件的命名规则url-loaderwebpack5 asset方式字体文件的打包PluginCleanWebpackPluginHtmlWebpackPluginDefinePluginCopyWebpackPluginmode配置webpack打包其他资源 图片资源 虽然此时我未安装file-loader 但是我正常显示了图片…

SHOP++ V9.1商城系统:可视化装修,0基础也能打造高颜值商城!

SHOP B2B2C商城系统新版本V9.1 新增的店铺装修功能是一款针对电商的DIY装修工具。可以快速装修店铺。能同时满足不同用户的使用需求。 一、页面可视化编辑组件 电商大潮异军突起的今天&#xff0c;如何让你的商品页面快速的抓住顾客的眼球&#xff0c;是促成订单转化的重要因素…

项目实战之旅游网(二)后台用户管理(上)

目录 一.管理员列表 二. 新增管理员 三.修改管理员 四.管理员详情 一.管理员列表 后台用户也称为管理员&#xff0c;每个管理员能在后台进行的操作不同,所以不同的管理员有不同的权限。在项目中&#xff0c;权限表的设计为用户-角色多对多&#xff0c;角色权限多对多&…

四、网络层(四)IPv6

目录 4.1 IPv6的主要特点 4.2 IPv6地址 4.3 从IPv4到IPv6过渡 4.1 IPv6的主要特点 解决IP地址耗尽问题的措施有以下3种 采用无分类域间路由&#xff0c;使IP地址的分配更加合理。采用网络地址转换NAT以节省全球IP地址。采用具有更大地址空间的新版本的IP协议IPv6。&am…

力扣(LeetCode)199. 二叉树的右视图(C++)

迭代 按照层序遍历&#xff0c;每一层最右边的元素就是二叉树右视图的元素。迭代算法需要队列保存二叉树每一层的所有结点&#xff0c;并且在遍历下一层时&#xff0c;上一层的所有结点已经出队。在遍历下一层之前&#xff0c;记录队列大小&#xff0c;即为二叉树当前层的结点…

三.keepalived介绍及工作原理

keepalived介绍及工作原理keepalived介绍及工作原理一、keepalived的介绍二、Keepalived服务的重要功能1、管理LVS负载均衡软件2、实现对LVS集群节点健康检查功能&#xff08;healthcheck&#xff09;3、作为系统网络服务的高可用功能&#xff08;failover&#xff09;三、Keep…

Redis 集合(Sorted Set)方法使用详解

目录一、简介二、常用方法2.1、ZADD2.2、ZREM2.3、ZSCORE2.4、ZINCRBY2.5、ZCARD2.6、ZRANK、ZREVRANK2.7、ZRANGE、ZREVRANGE2.8、ZRANGEBYSCORE、ZREVRANGEBYSCORE2.9、ZCOUNT2.10、ZREMRANGEBYRANK2.11、ZREMRANGEBYSCORE2.12、ZINTERSTORE、ZUNIONSTORE2.13、ZRANGEBYLEX、…

day 9 模拟和高精度

P4924 [1007]魔法少女小Scarlet 题目描述 Scarlet 最近学会了一个数组魔法&#xff0c;她会在 nn 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90∘。 首先&#xff0c;Scarlet 会把 1 到 n^2 的正整数按照从左往右&#xff0c;从上至下的顺序填入初始的二维数组中…

带您认识spreadsheet专属的数据仓库,助力报表开发好帮手

业务主题是针对业务的一个概念&#xff0c;它将同一数据源中属于同一个分析主题的表或表中的字段组合在一起&#xff0c;为进一步的可视化数据集提供基本元素。 业务主题我们看作是 Smartbi 产品中的数据仓库&#xff0c;主要应用于可视化数据集等操作。 业务主题的应用场景有如…