C++全面精通类与对象

 更新时间:2022年05月27日 11:43:17   作者:乔乔家的龙龙  
类和对象是两种以计算机为载体的计算机语言的合称。对象是对客观事物的抽象,类是对对象的抽象。类是一种抽象的数据类型;变量就是可以变化的量,存储在内存中—个可以拥有在某个范围内的可变存储区域

运算符重载

C++语法设计很巧妙,比如运算符重载一个 >

bool operator>(const Date& d)
{
     return !(*this <= d);
}

这里可以结合前面的内联函数来进一步提高代码的效率,而内联函数不支持 .h 和 .cpp 分开写,所以成员函数要成为内联函数最好的办法就是把定义放在类里面,类里面定义的会被默认为是 inline 内联函数。

我们计算日期类的加法时:

	Date Date::operator+(int d)
	{
		Date ret(*this);
		ret.day += d;
		while (ret.day > Getmonth(ret.year, ret.month))
		{
			ret.day -= Getmonth(ret.year,ret.month);
			ret.month++;
			if (ret.month == 13)
			{
				ret.year += 1;
				ret.month = 1;
			}
		}
		return ret;
	}

运算符复用

我们可能会有这样的问题,这里面 += 和 + 两个运算符其实是一样的,实现原理上没什么差别,那你可能会封装个函数来解决他们的关系,但是其实直接让他俩互相附庸,分为了 += 复用+ 和 + 复用 += 两种办法:

1.+= 复用 +:

2.+ 复用 +=(更优):

两种乍一看其实没什么区别,但是其实有优越和劣势可以分的,很 += 是不需要构造的,因为它是传引用调用(返回值为域外的 this 指针的内容,必须要传引用),但是 + 是必须要构造的,拷贝局部对象的 ret 和 最后的 return , 一共需要构造两次。

让 += 复用 +,+在先就会让整个过程构造 4 次,而让 + 来复用 += 的话,+ 还是构造 2 次没得说,但是 += 就不需要拷贝构造了,整个过程就只需要构造 2 次,消耗就会小很多。咱就应该多抠抠细节,写出正确的代码固然重要,但是追求更优秀更高效的代码是每一个程序员的基本素养。

前置后置运算符

既然 +,- 能造,那 ++ 和 – 自然也不在话下,但是这就不好玩了啊, num++ 和 ++num 功能上都是 +1,写成运算符重载格式都是

Date Date::operator++();

我们该怎么区分呢?要知道函数名相同而参数不同就应该敏感使用函数重载,C++这个大聪明是不会考虑不到这些的,因此就有了对应的语法:前置不带参数而后置带参数

Date operator++();//前置++

Date operator++(int d);//后置++

Date& operator++()//前置
{
   *this += 1;
   return *this;
}
Date operator++(int)//后置
{
    Date tmp(*this);
    *this += 1;
    return tem;
}

其实括号里面这个参数并没有任何意义,单纯只是用来区分前置与后置的写法,所以这里不写形参也是可以的,我这里就只给了一个类型。还有这里千万不要想着去加一个缺省值,显式传参还好,要是不传参编译器就没办法区分开来,属于是没事找事了。

const

给一个场景:

void Func(const Date& d)

{

d1.Print();

}

void test()

{

Date d1(2022,5,19);

d1.Print();

Func(d1);

}

这个场景下就会报错:

说实在的,这个报错我自己也看的云里雾里,为什么 Print 那里不报错到了 Func 里面 Print 就要报错?Print 传的过去 Func 就传不过去了?究其为什么会报错,其实涉及到一个权限问题。

void Print(Date* const this)
{
     cout<<year<<"-"<<month<<"-"<<day<<endl;
}

我们知道 Print() 的参数其实是 Date& const this ,在上面场景中去调用 Print 时其实参数是 &d1,传对象的地址。在 Print 定义时 const 修饰的是 this 指针,const 修饰的变量可以初始化,此时指针不能被改变但是他指向的内容可以被初始化和修改;而 Func 的 const 修饰 Date*,他指向的内容不能被修改,所以这是一个经典的权限放大问题。

const Date* 要传给 Date* ,所以我们需要一个 const 进行修饰保护,但是 this 本质是一个隐含形参,我们没办法显式调用,也就是说 const 没办法进行修饰。那么C++也提供了一种修饰方法打破这个僵局,就是在函数尾巴加上 const。

void Print() const
{}

尾巴上的 const 编译器就会默认你是加在了函数原本定义的前面,这样就完美了。

C++ 的IO流

我们在代码中使用的 << , >> 为流输入和流提取操作符,只要涉及输入或者输出数据,我们立马想到的就是 cin 和 cout,这俩货其实是全局的对象, cin 对应 istream 类,cout 对应 ostream 类,它们都声明在 头文件中,这也解释了“为什么在 C++ 程序中引入 就可以使用 cin 和 cout”。

我们之所以可以在 <<, >> 之后接上任何类型,是因为强大的语法对每种类型进行了重载,能自动识别类型的本质就是函数重载,所以如果一个 int 类型的流插入 cin<<1 其实是 cin . operator <<(1)。

初始化列表

与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段,初始化列表可以看成是对象的成员变量定义的地方:

class Func
{
public:
    Func(int a):
    _a(a){} // 初始化列表
private:
    int _a;
};

注意:每个成员变量在初始化列表中只能出现一次因为初始化只能初始化一次,还要明确哪些成员必须放在初始化列表进行初始化:

  1. 引用成员变量
  2. const 成员变量
  3. 自义定类型成员(该类没有默认构造函数)

其他变量即可以在初始化列表初始化也可以在函数体内初始化,内置类型成员不处理时,会调用默认构造函数即随机值,如果我给出缺省值,那么之这个值就是给初始化列表用的,如果在初始化列表也同时给出这个内置类型的初始化值,就会采用初始化列表的值。我们应该尽量在初始化列表就初始化完,这样能尽可能的减少很多毛病效率也高。

再来看看这个题目:

这个程序的结果是啥?

答案是 1 和随机值,因为成员变量在类中的声明次序就是他在初始化列表中的初始化顺序,与他在初始化列表中的先后次序无关。_a2 先声明 _a2 = _a1,此时 _a1 为随机值,所以 _a2 为随机值,_a1 为 1。

explicit 关键字

构造函数不仅可以构造和初始化对象,对于单个参数的构造函数,还具有类型转换的作用。在C语言里面我们就知道有隐式类型转换,其实在 C++ 里面也是一样的,比如针对我定义的一个 Date(int year):

Date d1(2022);
Date d2 = 2022;

显然, 这里 d2 需要的是 Date 类型的参数, 而我们传入的是一个int, 这个程序却能成功运行, 就是因为这隐式调用,另外说一句, 在对象刚刚定义时, 即使使用的是赋值操作符 = , 也是会调用构造函数, 而不是重载的 operator= 运算符。这两个语句对应前者是构造,而后者是构造+拷贝构造,相当于发生了隐式类型转换, 如果我们写成:

explicit Date(int year)

这个关键字会阻止这种转换的发生。

到此这篇关于C++全面精通类与对象的文章就介绍到这了,更多相关C++类与对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入解析C++编程中范围解析运算符的作用及使用

    深入解析C++编程中范围解析运算符的作用及使用

    这篇文章主要介绍了C++编程中范围解析运算符的使用方法,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • C语言中const和define的区别你了解嘛

    C语言中const和define的区别你了解嘛

    这篇文章主要为大家详细介绍了C语言中const和define的区别,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • C++非继承时函数成员访问属性和类继承过程中的访问控制

    C++非继承时函数成员访问属性和类继承过程中的访问控制

    这篇文章主要介绍了C++非继承时函数成员访问属性和类继承过程中的访问控制,非继承时,protected成员和private成员没有任何区别,都是类内部可以直接访问它们、类外部的类对象不可访问它们、类内部的类对象可以访问它们,更多详细内容请参考下面相关资料
    2022-03-03
  • 详解C++中递增运算符重载的实现

    详解C++中递增运算符重载的实现

    本文主要详解运算符重载里的递增运算符重载;递增和递减原理是一样的,这里就只分享递增的重载;提到递增递减,我们都知道又前置和后置两种方法, 那今天就详解一下前置递增和后置递增的细节,拿捏递增运算符重载
    2022-06-06
  • C语言+MySQL实现推箱子游戏

    C语言+MySQL实现推箱子游戏

    经典的推箱子是一个来自日本的古老游戏,目的是在训练你的逻辑思考能力。本文将通过C语言和MySQL实现推箱子这一经典游戏,感兴趣的可以了解一下
    2022-02-02
  • C++开发:为什么多线程读写shared_ptr要加锁的详细介绍

    C++开发:为什么多线程读写shared_ptr要加锁的详细介绍

    本篇文章介绍了,在C++中为什么多线程读写shared_ptr要加锁的详细说明。需要的朋友参考下
    2013-04-04
  • C++运行时获取类型信息的type_info类与bad_typeid异常

    C++运行时获取类型信息的type_info类与bad_typeid异常

    这篇文章主要介绍了C++运行时获取类型信息的type_info类与bad_typeid异常,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • 一文详解QDialog中exec与open的区别

    一文详解QDialog中exec与open的区别

    这篇文章主要为大家详细介绍了QDialog中exec与open的区别,文中的示例代码讲解详细,对我们学习Qt有一定的帮助,需要的可以参考一下
    2023-03-03
  • c语言单词搜索的实现

    c语言单词搜索的实现

    本文主要介绍了c语言单词搜索的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • C语言枚举与联合图文梳理讲解

    C语言枚举与联合图文梳理讲解

    枚举顾名思义就是把所有的可能性列举出来,像一个星期分为七天我们就可以使用枚举,联合体是由关键字union和标签定义的,和枚举是一样的定义方式,不一样的是,一个联合体只有一块内存空间,什么意思呢,就相当于只开辟最大的变量的内存,其他的变量都在那个变量占据空间
    2023-01-01

最新评论