C++ 类对象

news2024/11/19 17:43:44

C++是一种功能强大的编程语言,它拥有丰富的特性集合,使得我们可以编写出高效、可维护且性能卓越的代码。其高级概念包括运算符重载、静态成员、友元函数、匿名对象和嵌套类。这些概念在面向对象编程中扮演着至关重要的角色,它们提供了对对象行为和相互作用的灵活控制。为了更好地解释构成C++类的各个部分,我将创建一个日期类作为示例。

class Date
{
public://公有,声明函数
	void Print();
private://私有,保存对象的数据
	int _year;
	int _month;
	int _day;
};

在开始之前,我们需要设计一个函数用获得一年中某个月的天数,用来方便我们进行进行日期的计算 

int Date::getMonthdays(const int year, const int month)
{
	int months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0))
	{
		return 29;
	}
	
	return months[month];
}

运算符重载

在C++中,运算符重载允许程序员定义现有运算符的新行为,特别是对于用户定义的数据类型。要重载一个运算符,需要在类或结构体内部声明一个特殊的运算符函数。例如,如果想要重载加法运算符`+`,可以在类中定义如下函数:

returnType operator+(argumentType)

这里,returnType是函数的返回类型,operator是关键字,+是要重载的运算符,而argumentType是传递给函数的参数类型。这种方法使得代码更直观,易于理解,同时也保持了操作的一致性和直观性。

+,+=

我们现在有一个表示日期的类,你可能希望使用加号运算符(`+`)来实现日期对象和整形数据的相加。

Date d1(1970, 1, 1);//构造d1


Date d2 = d1 + 100;
d1 += 100;

我们想要实现+,+=,但是我们要知道他们的区别,+号并不会改变原本的d1的值,而是会返回一个与d1对象一样的,但日期已经加过的值+=则是会改变原本的值,也就是说,他们的返回值一个是拷贝计算后的值,一个是本体!

//Date Date::operator+(int day)//我们设计一个日期对象计算后返回
//{
//	Date tmp(*this);
//
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		tmp._month++;
//		if (tmp._month == 13)
//		{
//			++tmp._year;
//			tmp._month = 1;
//		}
//	}
//
//	return tmp;
//}


Date Date::operator+(int day) const//但也可以减少拷贝对象,调用+=的实现
{
	Date temp = *this;
	return temp += day;
}
Date& Date::operator+=(int day)
{

	if (day < 0)//因为+可以进行+负数运算,我们可以单独实现后再这里复用函数
	{
		*this -= -day;
		return *this;
	}

	_day += day;

	while (_day > getMonthdays(_year, _month))
	{
		++_month;

		if (_month == 13)
		{
			++_year;
			_month = 1;
		}

		_day -= getMonthdays(_year, _month);
	}

	return *this;
}

前置++与后置++ 

前置++返回是已经加过的值

Date& Date::operator++()//也就是加一,我们可以直接调用+=的函数
{
	(*this) += 1;
	return *this;
}

 后置++ 返回时没有加的值,但是本体已经改变

Date Date::operator++(int)
{
	Date temp = *this;

	(*this) += 1;
	return temp;
}

他们因为运算符一致,所以后置++函数参数用int进行区别 

比较运算符重载

Date d1(1970, 1, 1);
Date d2(1999, 1, 1);
d2 < d1;

这些运算符通常返回布尔值(bool类型),表示比较的结果是真(true)还是假(false

bool Date::operator<(const Date& d) const
{
	return _year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day);
}

const的作用是因为我们比较并不需要改变操作值的大小,而操作值可能是const对象,无法改变,防止错误,对于一边=般不会改变操作值的函数,我们可以对其加上const防止我们代码失误改变大小后及时发现! 

<<  >>  重载

我们同样可以将“流提取运算符”和“流插入运算符”重载

ostream& operator<<(ostream& out)
{
	out << _year << "\\" << _month << "\\" << _day;
	out << endl;

	return out;
}


istream& operator>>(istream& in)//不要const 
{
	in >> _year >> _month >> _day;

	return in;
}

但是这样写很不符合我们的使用,我们想要

cout << d1;
cin >> d1;

 但是,这个类里面的运算符我们只能

d1 << cout;
d1 >> cin;
d1.operator<<(cout);
d1.operator>>(cin);

这样很不符合我们的使用习惯!

运算符重载不仅限于算术运算符,重载比较运算符还可以赋值运算符、下标运算符等,但是

在C++中,有些运算符是不能被重载的,这主要是因为它们与语言的基本结构和内存模型紧密相关。以下是一些不能被重载的运算符:

- 范围解析运算符(::)
- 三元条件运算符(?:)
- 成员访问运算符(.)
- 成员指针访问运算符(.*)
- 对象大小运算符(sizeof)
- 对象类型运算符(typeid)
- 所有形式的类型转换运算符,包括 static_cast、dynamic_cast、const_cast 和 reinterpret_cast

这些限制确保了C++的某些关键特性不会因为运算符重载而变得不确定或混乱。

运算符重载应该谨慎使用,因为如果没有合理的设计,它可能会使代码变得难以理解和维护。在设计重载运算符时,应确保它们的行为与它们的原始意图相符,以避免混淆。

静态成员

在C++编程领域,静态成员占据着独特的位置。它们是类成员,被类的所有实例共享。与每个对象都独有的非静态成员不同,静态成员无论创建多少个类的对象,都作为单一副本存在。

静态数据成员

静态数据成员是属于类而不是任何对象的变量。这意味着即使类的所有对象都被销毁,它们也保留它们的值。它们特别适用于存储需要所有对象访问的类范围信息。

例如,考虑一个表示3D盒子的`Box`类。如果我们想要跟踪创建了多少个`Box`对象,我们可以使用一个静态数据成员来存储计数。这里是一个简化的例子:

class Box {
public:
    static int objectCount;
    // 构造函数增加对象计数
    Box() {
        objectCount++;
    }
};

// 初始化静态成员
int Box::objectCount = 0;

在这个例子中,`objectCount`在所有`Box`实例中共享,并且每次创建新的`Box`时都会递增。

静态成员函数

另一方面,静态成员函数可以在没有类的对象的情况下被调用。它们只能访问静态数据成员和其他静态成员函数。这些函数不绑定到任何特定对象,因此没有`this`指针。

静态成员函数可以用来访问`objectCount`静态数据成员,例如:

class Box {
public:
    static int objectCount;
    // 静态函数访问objectCount
    static int getObjectCount() {
        return objectCount;
    }
};

int Box::objectCount = 0;

使用和考虑

他们可以解决一个经典问题,计算从1+到n,不能使用循环,迭代......我们可以通过查看静态变量,每当调用一次静态变量,就加一,sum记录加的总数,就可以解决这个问题

当你需要维护对类的所有对象共同的状态或行为时,使用静态成员可以使代码更高效。然而,重要的是要谨慎使用它们。过度使用静态成员会使你的代码变得不够灵活,更难以测试。

此外,静态成员在程序执行开始之前就初始化为零或构造,在进入`main()`之前。这有时会导致所谓的“静态初始化顺序混乱”,即跨翻译单元的静态变量初始化顺序是未定义的。总之,静态成员是C++的一个强大功能,正确使用时,可以帮助以高效的方式管理共享数据和功能。

友元

友元函数

在C++编程领域,友元函数的概念是一个有趣的特性,它允许函数从类的外部访问类的私有和受保护成员。这一能力在我们需要允许某些非成员函数访问类的私有数据而不破坏封装和数据隐藏原则的情况下特别有用。

C++中的友元函数是一种虽然不是类的成员,但被授予访问类的私有和受保护成员的权限的函数。它在类定义中用`friend`关键字声明,其原型出现在类中,但其定义在外部。

要声明友元函数,你只需在类的私有成员需要访问的类中,用`friend`关键字预先声明函数原型。以下是一个简单的例子:

class MyClass {
private:
    int secretValue;

public:
    friend void friendFunction(MyClass &obj);
};

void friendFunction(MyClass &obj) {
    // 访问MyClass的私有成员
    std::cout << "Secret Value is: " << obj.secretValue << std::endl;
}

我们也可以通过友元函数来重载cout cin函数

//内联函数
inline ostream& operator<<(ostream& out, const Date& d)//减少拷贝
{
	out << d._year << "\\" << d._month << "\\" << d._day;
	out << endl;

	return out;
}

inline istream& operator>>(istream& in, Date& d)//不要const 
{
	in >> d._year >> d._month >> d._day;

	return in;
}

 我们就可以这样调用了

cout << d1;
cin >> d1;

友元函数的好处有:

1. 灵活性:友元函数提供了一个替代成员函数访问私有数据的方法,这使得设计变得更加灵活。有时候,某些操作可能更自然地作为一个独立的函数存在,而不是作为一个类的成员函数。

2. 效率:与成员函数相比,友元函数可以更高效。因为它们不需要通过对象来访问私有数据,避免了成员函数调用的开销,从而提高了执行效率。

3. 实用性:特别是在操作符重载方面,友元函数非常有用。有时,某些操作需要访问不同类的私有数据,而这些数据不能通过成员函数访问。

考虑到最佳实践:

- 保持封装:应该谨慎使用友元函数,以保持面向对象编程的封装性。友元函数应该仅在必要时使用,以防止破坏类的封装性。
  
- 限制访问:应该仅授予必须访问类敏感数据的函数友元状态。这样可以最小化友元函数对类的影响,同时保持类的封装性。

- 文档化:在代码中清晰地记录授予友元访问权限的原因是很重要的。这样做可以帮助保持代码的清晰性,并使其他开发人员更容易理解友元函数的用途。

C++中的友元函数提供了一个受控的封装突破口,允许外部函数与私有类数据交互。当适当使用时,它们可以导致更高效和灵活的代码。然而,关键是要谨慎使用这一功能,以保持面向对象设计原则的完整性。

友元类

友元类的声明相当直接。在共享其成员的类内部,通过`friend`关键字后跟类名来声明友元类。以下是一个简单的例子来说明这一点:

class ClassA {
private:
    int data;

public:
    ClassA(int value) : data(value) {}

    friend class ClassB; // 声明ClassB为友元类
};

class ClassB {
public:
    void showData(ClassA& a) {
        // 访问ClassA的私有数据
        std::cout << "ClassA data: " << a.data << std::endl;
    }
};

在上面的例子中,ClassB是ClassA的朋友,这意味着它可以访问ClassA的私有成员data。但是,ClassA不能访问ClassB!这是一个强大的功能,但应谨慎使用。友元类的使用可能会破坏封装,这是面向对象编程的核心原则。因此,重要的是只有在绝对必要时,以及当其他设计模式或技术不足以满足需求时,才使用这个功能。

友元类常见的使用场景之一是在实现复杂数据类型的操作符重载时。有时,操作函数需要访问类的私有部分以执行其操作,声明操作函数为友元允许这种访问。

值得注意的是,友元关系不是传递的或继承的。如果ClassC是ClassB的朋友,而ClassB是ClassA的朋友,ClassB并不自动成为ClassA的朋友。C++中友元类和函数的概念证明了该语言的灵活性及其满足广泛编程需求的能力。但同时破坏了C++的私有有封装,使用友元的时候一定要注意!

匿名对象

匿名对象通常用于即时的一次性操作,其中创建对象,使用后即被丢弃,无需持久的引用。这在只需要对象来调用成员函数或作为另一个函数的参数传递的场景中特别有用。

在C++中创建匿名对象非常直接。它涉及实例化一个类而不将结果对象分配给变量。例如:

class MyClass {
public:
    MyClass() { /* 构造函数代码 */ }
    void display() const { /* 显示一些内容 */ }
};

// 创建一个匿名对象并调用成员函数
MyClass().display();

在上面的代码片段中,`MyClass().display();` 创建了一个`MyClass`的匿名对象,并立即调用它的`display`方法。该语句执行后,匿名对象超出作用域并被销毁。

使用匿名对象的主要优点之一是减少内存消耗,因为这些对象是临时的,不会超出它们使用的范围。它们还通过消除在不需要后续引用的情况下命名变量的需要,简化了代码。

然而,重要的是要注意匿名对象有其局限性。由于它们没有分配给变量,它们在初始使用后不能被访问。这意味着除非已经使用或存储在其他地方,否则对象内的任何状态更改或信息都会丢失。

在实践中,可以在各种应用中看到匿名对象,例如在操作符重载中,它们在操作期间作为值的临时载体。它们在函数调用中也很有用,在这些调用中需要对象作为参数,但之后不再需要。

class MyClass {
public:
    MyClass(int a) { /* 构造函数代码 */ }
    void display() const { /* 显示一些内容 */ }
    int A()
    {
        return 2;
    }
};


MyClass fun()
{
    int b=MyClass().A();
    return MyClass(b);
}

内部类

嵌套类

C++中的嵌套类可以被视为一种将类声明以逻辑和可管理的方式分组的方法。它们允许在类中定义类,这可以导致更可读和可维护的代码。内部类可以访问外部类的私有和受保护成员,作用有点像友元类,但重要的是要注意,这种关系不是相互的;外部类没有特殊的访问内部类的私有成员的权限。

语法和可访问性

定义嵌套类时,语法很直接。内部类在外部类的主体内声明。它可以在公共、受保护或私有部分声明,这取决于所需的可访问性级别。如果内部类在公共部分声明,可以使用作用域解析运算符实例化,如此:`OuterClass::InnerClass obj;`。然而,如果它在私有部分声明,则不能被外部代码直接实例化,这对于某些设计模式可能很有用。

class OuterClass {
    int outerData;

    class InnerClass {
    public:
        void innerFunction(OuterClass& outer) {
            outer.outerData = 100; // 访问OuterClass的私有成员
        }
    };

public:
    void outerFunction() {
        InnerClass inner;
        inner.innerFunction(*this);
    }
};

int main() {
    OuterClass outer;
    outer.outerFunction();
    return 0;
}

在这个例子中,InnerClass是OuterClass内的一个嵌套类,并且有一个公共函数innerFunction,它接受一个OuterClass对象的引用。这个函数可以修改OuterClass的私有数据,展示了两个类之间的紧密关系。

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

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

相关文章

Maxwell安装使用和简单案例

一、解压 cd /opt/software/ ​ tar -zxvf maxwell-1.29.2.tar.gz -C /opt/module/ ​ cd /opt/module/ 二、MySQL 环境准备 1、修改 mysql 的配置文件 修改 mysql 的配置文件&#xff0c;开启 MySQL Binlog 设置 vi /etc/my.cnf 添加以下内容 server_id1 log-binmysql-…

一篇文章带你掌握⽹络原理--HTTP/HTTPS(HTTP各个部分详解和HTTPS加密详解)

HTTP HTTP是什么? HTTP(全称为"超⽂本传输协议")是⼀种应⽤⾮常⼴泛的应⽤层协议. HTTP诞⽣与1991年.⽬前已经发展为最主流使⽤的⼀种应⽤层协议. 最新的HTTP3版本也正在完善中,⽬前Google/Facebook等公司的产品已经⽀持了. HTTP往往是基于传输层的TCP协议实现的…

软件测试方法汇总

种类繁多&#xff0c;记忆起来混乱&#xff0c; 如果把软件测试方法进行分类, 就会清晰很多。 我参考一些书籍和网上的资料&#xff0c; 把常用的软件测试方法列出来&#xff0c; 让大家对软件测试行业有个总体的看法。 从测试设计方法分类 总结&#xff1a; 实际工作中&…

29. 【Android教程】折叠列表 ExpandableListView

本节学习一个可折叠的 ListView&#xff0c;可以用在一些需要分类的场景下。通过 ExpandableListView 我们可以首先在 ListView 上展示大的分类&#xff0c;当点击某个类别的时候再将 ListView 做一个展开&#xff0c;展示该类下的所有子类供用户选择。它与 ListView 的不同主要…

css样式,经典老问题回顾

跑到了一堆来了 这时需要在父div加相对定位的css 传说中的 子绝父相

Python Flask Web框架快速入门

Flask 入门Demo Flask 开发环境搭建&#xff0c;执行如下指令&#xff1a; pip install flask # 第一节: Flask 快速入门from flask import Flask app Flask(__name__)app.route(/flask) def hello_flask():return Hello Flaskapp.run() 核心代码剖析&#xff1a; 从 fla…

微信小程序开发之多图片上传+.NET WebAPI后端服务保存图片资源

前言&#xff1a; 最近开发的一个微信小程序项目需要做一个同时选中三张&#xff08;或者是多张&#xff09;图片一起上传到服务端&#xff0c;服务端保存图片资源并保存的功能。发现在微信小程序开发中会有很多场景会使用到多图片上传并保存到的功能&#xff0c;所以我把自己总…

酷开科技抓住“客厅经济”发展的机遇,不断对酷开系统升级赋能

酷开科技抓住“客厅经济”发展的机遇&#xff0c;不断对酷开系统升级赋能&#xff0c;打造新的生活场景&#xff0c;满足消费者的不同生活需求&#xff0c;酷开科技的产品和服务让消费者能够在家庭空间中享受到更加智能、便捷和温馨的时光。同样凭借更加包容、开放的生态体验&a…

unordered_map 与map使用说明

目录 背景&#xff1a; 问题分析&#xff1a; 使用APE工具&#xff0c;查看录制的原始门信号是否存在异常 查看报文读取是否存在问题 分析报文读取代码 为什么在Windows系统中&#xff0c;解析后的门信号没有存在跳变情况 是否Windows 和Linux下 unordered_map中数据先后…

文本批量高效编辑管理,支持将文本进行自定义行数进行拆分,实现文本的高效管理

在信息爆炸的时代&#xff0c;文本文件的管理和编辑成为了许多工作和学习中不可或缺的一部分。面对大量的文本内容&#xff0c;如何高效地进行编辑和管理成为了一个挑战。现在&#xff0c;我们为您带来了一款强大的批量文本编辑管理工具&#xff0c;支持自定义行数拆分&#xf…

面试题集中营—分布式共识算法

分布式共识算法目标 分布式主要就是为了解决单点故障。一开始只有一个服务节点提供服务&#xff0c;如下图所示。那么如果服务节点挂了&#xff0c;对不起等着吧。 为了服务的高可用性&#xff0c;我们一般都会多引入几个副节点当备份&#xff0c;当服务节点挂了&#xff0c;就…

BRC20铭文铭刻解析

BRC20铭文铭刻的出现对于智能制造无疑是一个重要的里程碑。随着科技的飞速发展&#xff0c;智能制造已经成为制造业发展的必然趋势&#xff01;智能制造是指通过运用人工智能、物联网、大数据等先进技术&#xff0c;实现生产过程的自动化、智能化和高效化。 1. BRC20铭文的概念…

2024常见的自动化测试工具和框架!

在软件测试领域&#xff0c;自动化测试框架有很多&#xff0c;这里主要介绍几种常用的自动化测试框架。 以下是几种常用的自动化测试框架&#xff1a; Selenium&#xff1a;Selenium 是一个功能强大的Web应用程序测试框架&#xff0c;支持多种编程语言&#xff0c;如Java、Pyt…

在ELF 1开发环境中使用Qt Creator进行远程调试

Qt Creator是一款跨平台集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要适用于支持Qt框架的各类应用程序开发。其内置的远程调试机制使得开发者能够在本地开发环境中对部署在远程设备上的代码进行调试&#xff0c;无需直接对远程设备进行操作。Qt Creator会通过网络连…

Django中的实时通信:WebSockets与异步视图的结合

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…

将图片按灰度转换成字符

from struct import *ch [., :, !, ~, ,, ^, *,$, #]//可以在这里面由浅到深添加字符 ch.reverse()def calc(R, G, B):#模式Lreturn R * 299 // 1000 G * 587 // 1000 B * 144 / 1000def character( val):num val / 260 * len(ch)num round(num)if num>len(ch):numlen(…

【H4012】3.3V5V12V24V30V,3.5A大电流温度低 高效同步降压芯片IC DC-DC

您提到的“3.3V, 5V, 12V, 24V, 30V, 3.5A 高效同步降压芯片IC DC-DC”是指一种能够将较高电压&#xff08;例如24V或30V&#xff09;降至较低电压&#xff08;例如3.3V, 5V或12V&#xff09;的直流-直流&#xff08;DC-DC&#xff09;转换器。这种转换器通常使用同步降压技术&…

uniapp 安卓批量异步权限授权,没有授权就跳系统App设置页

首先需要一个js的sdk&#xff1a;App权限判断和提示 - DCloud 插件市场 下载下来&#xff0c;引入里面的 permission.js 示例代码&#xff1a; <script>import { requestAndroidPermission } from ./sdk/permission.jsexport default {onLaunch(e) {const getMutiPer…

kerberos:适配华为FI

文章目录 一、hive1、hive thrift连接方式 一、hive 1、hive thrift连接方式 kerberos认证失败信息 缺少配置&#xff1a;{“hadoop.rpc.protection”:“privacy”}&#xff0c;具体可参考&#xff1a;kerbros认证相关问题 华为FI参考资料&#xff1a; https://github.com…

从零开始学习Linux(4)----yum和vim

1.Linux软件包管理器yum Linux中我们要进行工具/指令/程序&#xff0c;安装&#xff0c;检查卸载等&#xff0c;需要yum的软件 安装软件的方式&#xff1a; 源代码安装---交叉编译的工具rpm包直接安装yum/apt-get yum是我们Linux预装的一个指令&#xff0c;搜索&#xff0c;下…