【C++】类和对象(下)

这篇具有很好参考价值的文章主要介绍了【C++】类和对象(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘。
🛸C++专栏:C++内功修炼基地
家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

【C++】类和对象(下)

1、再谈构造函数

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 初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟

一个放在括号中的初始值或表达式。

【注意】

  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 = 1; // const 
};

注意:引用成员变量和const成员变量需要在初始化列表进行初始化

对于自定义类型,在没有默认构造函数的时候就需要用初始化列表

进行初始化,如果右默认构造则可以不写。如果在声明处给值,

则是缺省值,提供给初始化列表的。

  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,

一定会先使用初始化列表初始化。

  1. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序

    与其在初始化列表中的先后次序无关。

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  随机值

注意:这时候_a1还是随机值,所以按声明顺序初始化,_a2被初始化为

随机值,_a1被初始化为1,所以选择D。

1.3 explicit关键字

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

class Date
{
public:
	// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
 // explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
	explicit Date(int year)
		:_year(year)
	{}
	/*
	// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
   有类型转换作用
	// 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;
};
void Test()
{
	Date d1(2022);
	// 用一个整形变量给日期类型对象赋值
	// 实际编译器背后会用2023构造一个匿名对象,最后用无名对象给d1对象进行赋值
	d1 = 2023;
	// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,
	//禁止了单参构造函数类型转换的作用
}

上述代码可读性不是很好,用explicit修饰构造函数,将会禁止构造函数的隐式转换

2、 static成员

2.1 概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

int _scount = 0;
class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	/*static int GetACount() { return _scount; }*/

	//static int _scount;
};

A aa0;
void Func()
{
	static A aa2;
	cout << __LINE__ << ":" << _scount << endl;

	// 全局变量的劣势:任何地方都可以随意改变
	_scount++;
}

int main()
{
	cout <<__LINE__<<":"<< _scount << endl;  // 1
	A aa1;
	
	Func();  // 3
	Func();  // 4

	return 0;
}

【C++】类和对象(下)

注意:这里用全局变量进行计算创建了多少个对象,这里为什么不是1 3 5呢?

因为func函数里面是用静态创建的类对象,所以创建了一次不会销毁,而是

程序结束才会销毁,所以第二次调用func的时候不会进行调用构造。

class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	/*static int GetACount() { return _scount; }*/

	static int _scount;
};

A aa0;
int A::_scount = 0;
void Func()
{
	static A aa2;
	cout << __LINE__ << ":" << A::_scount << endl;

	// 全局变量的劣势:任何地方都可以随意改变
	//int _scount++;
}

int main()
{
	cout <<__LINE__<<":"<< A::_scount << endl;  // 1
	A aa1;
	
	Func();  // 3
	Func();  // 3

	return 0;
}

【C++】类和对象(下)


用类封装静态成员变量,不要让任何地方都可以改变

2.2 特性

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区

  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

  3. 类静态成员即可用 类名: :静态成员 或者 对象 . 静态成员来访问

  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

【问题】

  1. 静态成员函数可以调用非静态成员函数吗?

    不可以。非静态的成员函数调用需要this指针,我没有this

  2. 非静态成员函数可以调用类的静态成员函数吗

    ​ 可以

3、友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以

友元不宜多用。

友元分为:友元函数友元类

3.1 友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部普通函数,不属于任何类,但需要在

类的内部声明,声明时需要加friend关键字。

说明:

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

3.2 友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

  • 友元关系是单向的不具有交换性

比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接

访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

  • 友元关系不能传递

如果C是B的友元, B是A的友元,则不能说明C时A的友元。

  • 友元关系不能继承,在继承位置再给大家详细介绍。
class Time
{
	friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
	中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

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

4、内部类

概念如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,

它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越

的访问权限。

注意内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访

问外部类中的所有成员。但是外部类不是内部类的友元。

特性

  1. 内部类可以定义在外部类的public、protected、private都是可以的。

  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。

  3. sizeof(外部类)=外部类,和内部类没有任何关系。

class A
{
private:
 	static int k;
 	int h;
public:
 class B // B天生就是A的友元
 {
 public:
 	void foo(const A& a)
 	{
     cout << k << endl;//OK
     cout << a.h << endl;//OK
 	}
 };
};
int A::k = 1;
int main()
{
    A::B b;
    b.foo(A());
    
    return 0;
}

5、匿名对象

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;
	}
};
int main()
{
	A aa1;
	// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
	A();
	A aa2(2);
	// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说
	Solution().Sum_Solution(10);
	return 0;
}

6.对象时的一些编译器优化

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

是非常有用的。

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 aa1;
	f1(aa1);
	cout << endl;
	// 传值返回
	f2();
	cout << endl;
	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);
	// 一个表达式中,连续构造+拷贝构造->优化为一个构造
	f1(A(2));
	cout << endl;
	// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
	A aa2 = f2();
	cout << endl;
	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;
	return 0;
}

【C++】类和对象(下)文章来源地址https://www.toymoban.com/news/detail-447088.html

到了这里,关于【C++】类和对象(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】:类和对象(1)

    朋友们、伙计们,我们又见面了,本期来给大家解读一下有关C++中类和对象的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏: C语言:从入门到精通 数据结构专栏: 数据结构 个  人  主  页 : stackY、 目录 1.面向过程和面

    2024年02月08日
    浏览(27)
  • 【C++】类和对象-封装

    在main函数前重新补上isSame函数 在Cube类里面添加issamebyclass,利用成员函数判断两个立方体是否相等 自己写的代码: B站视频链接: https://www.bilibili.com/video/BV1et411b73Z/?p=105spm_id_from=333.1007.top_right_bar_window_history.content.clickvd_source=fb8dcae0aee3f1aab700c21099045395

    2024年02月15日
    浏览(34)
  • 【c++】类和对象1

    C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完 成 C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。

    2024年01月23日
    浏览(24)
  • 【C++】类和对象(一)

    目录 🍊一.面向过程和面向对象的初步认识🍊 🍋二.类的引入🍋 🍍三.类的定义🍍 🥭四.类的访问限定符及封装🥭 4.1访问限定符 4.2class和struct的区别 4.3封装 🍎五.类的作用域🍎 🍏六.类的实例化🍏 🍐七.类对象模型🍐  7.1怎么计算类对象的大小 7.2类对象的存储方式 🍑

    2024年02月05日
    浏览(23)
  • 【C++】类和对象(3)

    首先我们先回顾一下构造函数,对象的初始化由构造函数来完成,我们可以在构造函数的函数体内对对象的成员变量进行赋值,但这就有一个问题,如下: 答案:显然不是,因为变量只能定义一次。 也就是说,构造函数的函数体内部并不是初始化的地方(定义的地方),而

    2024年02月06日
    浏览(25)
  • 【C++】类和对象(下)

    在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化, 构造函数体中的语句只能将其称为赋初值 ,而不能称作初始化。因为初始化只能

    2024年03月25日
    浏览(30)
  • 【C++】类和对象(下)

    🌇 个人主页 :平凡的小苏 📚 学习格言 :命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘。 🛸 C++专栏 :C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我

    2024年02月05日
    浏览(27)
  • 【C++】类和对象(上篇)

    🚩 C语言 是 面向过程 的, 关注 的是 过程 ,分析出求解问题的步骤,通过函数调用逐步解决问题 🚩 C++ 是基于 面向对象 的, 关注 的是 对象 ,将一件事情拆分成不同的对象,靠对象之间的交互完成 🌰 比如: C语言结构体中只能定义变量, 在C++中,结构体内不仅可以定

    2024年01月22日
    浏览(27)
  • C++:类和对象(中)

    如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时, 编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数 class Date {}; 对于Date类,可以通过 Init 公有方

    2024年02月03日
    浏览(28)
  • C++类和对象(上)

    ✨Blog:🥰不会敲代码的小张:)🥰 🉑推荐专栏: C语言 🤪、 Cpp 😶‍🌫️、 数据结构初阶 💀 💽座右铭:“ 記住,每一天都是一個新的開始😁😁😁 ” 💀本章内容: 《C++类和对象(上)》的介绍✨ C语言是面向过程的,关注的是过程,分析出求解的步骤,通过函数逐步解

    2024年01月25日
    浏览(19)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包