C++:类与对象(下)

前言:

前言:

上一篇博客我们介绍了类与对象中的几类默认成员函数,这篇让我们继续来学习类与对象吧!

个人主页:Pianeers

文章专栏:C++

如果有问题,欢迎评论区讨论!

希望能帮到大家,求点赞,关注加收藏!

目录

一、初始化列表

二、类型转换 

 三、static

 四、友元

五、内部类

 六、匿名对象

 七、拷贝对象时优化


一、初始化列表

我们之前的默认构造函数是用来初始化的,可以在函数内给成员变量初始化,但如果有一些特殊的成员呢?如:const成员变量,没有默认构造的类类型变量。

这时就要从之前说起,成员变量在private中只是声明并没有被定义,当创建一个对象时,成员变量需要被定义,这时要介绍初始化列表。

之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅式,就是初始化列表,所以初始化列表不仅可以定义还可以初始化

所以成员变量都要初始化列表中定义,初始化没有太多限制,但是有一些必须要在初始化列表中初始化。

①const成员变量

② 没有默认构造的类类型变量

③引⽤成员变量

它们都是被要求在定义的同时就要进行初始化的。

 

初始化列表的特点:

①初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。

 ②每个成员变量在初始化列表中只能出现⼀次,因为语法理解上初始化列表认为是每个成员变量定义初始化的地⽅。

③成员变量的初始化与在类中的声明顺序有关,与在初始化列表中的顺序无关。

 以下还有几个需要注意的点:

①C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的成员使⽤的。

其中该缺省值的作用是:如果初始化列表没有显⽰初始化,默认就会⽤这个缺省值初始化。

class Time
{
public:
	Time(int hour)
		:_hour(hour)
	{
		cout << "Time()" << endl;
	}
private:
	int _hour;
};
class Date
{
public:
	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// 注意这⾥不是初始化,这⾥给的是缺省值,这个缺省值是给初始化列表的
// 如果初始化列表没有显⽰初始化,默认就会⽤这个缺省值初始化
	int _year=2024;
	int _month=7;
	int _day=16;
	Time _t=1;
	const int a=1;
	int* tmp = (int*)malloc(12);
};
int main()
{
	int i = 0;
	Date d1;
	d1.Print();
	return 0;
}

我个人建议能使用初始化列表初始化就用初始化列表,因为每个成员变量都要在初始化列表中定义 。

初始化列表中情况分析:

1.在初始化列表中初始化的成员。(显示写出来的)

2.没在初始化列表中初始化的成员。(没有写出的)

→→在声明时有缺省值时,用缺省值进行初始化。

→→②没有缺省值。

{

→→→①内置类型,大概率随机值,看编译器。

→→→②自定义类型,调用默认构造,没有则报错。

}

二、类型转换 

C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

如下:

当1这个类型直接拷贝构造给a时,1构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造a。

当你不希望这种隐式类型转换发生,可以构造函数前⾯加explicit就不再⽀持隐式类型转换

class A
{
public:
	 A(int a)
		:_a1(a)
	{
	}
	void print()
	{
		cout << _a1 << _a2 << endl;
	}
private:
	int _a1= 1;
	int _a2 = 2;
};
int main()
{
	A a = 1;
	a.print();
	return 0;
}

 三、static

在之前的C语言的学习中,我们了解了static关键字的作用是在程序中标记变量、方法或代码块为静态,同样的它在类中同样可以标记成员变量为静态

static的特点:

①⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化

②⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针,所以静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针。

③⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。

静态成员也是类的成员public、protected、private 访问限定符的限制。可以通过类名::静态成员 或者 对象.静态成员访问任意的静态成员变量和静态成员函数。

静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区;静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表。

class B
{
public:
	B()
	{
		++a;
	}
	B(const B& c)
	{
		++a;
		cout << "kaobe" << endl;
	}
	int rmid()
	{
		//⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数
		mid();
		return a;
	}
	static int mid()
	{
		//只能访问静态的
		return a;
	}
private:
	//类里面声明
	static int a;
	int g = 0;
};
//类外面初始化
//突破类域就可以访问静态成员
int B::a=0;
int main()
{
	B b;
	cout << b.mid() << endl;
	B d(b);
	cout << d.mid() << endl;
}

 四、友元

当我们创建一个函数不在类以内时,因为成员变量被private修饰,所以成员变量不可以在函数内被访问,这时我们可以通过友元的方式,友元提供了⼀种突破类访问限定符封装的⽅式。

友元是一种很有效的方式,但是并不推荐使用,因为它会破坏封装

友元的特点:

①友元分为:友元函数和友元类,在函数声明或者类 声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。

②外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。

③友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。

④⼀个函数可以是多个类的友元函数。

⑤友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。

⑥友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是C的友元。

class B;
class A
{
	friend void func(const A& aaa,const B& bbb);
private:
	int a1;
	int a2;
};
class B
{
	friend void func(const A& aaa, const B& bbb);
private:
	int b1;
	int b2;
};
void func(const A&aaa,const B&bbb)
{
	cout << aaa.a1 << aaa.a2 << endl;
	cout << bbb.b1 << bbb.b2 << endl;
}
int main()
{
	A aa;
	B bb;
	func(aa, bb);
}

五、内部类

如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。

内部类的特点:
①内部类是⼀个独⽴的类,跟定义在全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类,sizeof(外部类)=外部类。

内部类默认是外部类的友元类。但内部类可以直接访问外部类中的static成员

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

class A
{
private:
	static int _k;
	int _h = 1;
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()
{
	cout << sizeof(A) << endl;
	A::B b;
	A aa;
	b.foo(aa);
	return 0;
}

 六、匿名对象

⽤ 类型(实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的 叫有名对象;匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

class A
{
public:
	A(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	 }
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	//无类型名
	A(2024, 7, 18);
}

 七、拷贝对象时优化

现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传值 过程中可以省略的拷⻉。

class A
{
public:
	A(int a=100)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A & aa)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a=20;
};
A func1()
{
	A c;
	return c;
}
int main()
{
	//A a;
	//A b(a);
	func1();
}

本来函数调用:先构造c、返回值时创建一个c的临时对象。应该是两次构造,2次析构。

但优化后只有一次构造和一次析构

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-22 14:42:04       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-22 14:42:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-22 14:42:04       45 阅读
  4. Python语言-面向对象

    2024-07-22 14:42:04       55 阅读

热门阅读

  1. [算法题]mari和shiny

    2024-07-22 14:42:04       17 阅读
  2. 面试官:你对ConcurrentHashMap了解多少?

    2024-07-22 14:42:04       17 阅读
  3. 封装的通用链表(list.c/list.h/test_list.c)

    2024-07-22 14:42:04       17 阅读
  4. 将SQL中的占位符替换成参数

    2024-07-22 14:42:04       14 阅读
  5. 前端控制器模式

    2024-07-22 14:42:04       21 阅读
  6. Redis

    Redis

    2024-07-22 14:42:04      17 阅读
  7. Vue3升级了哪些重要的功能

    2024-07-22 14:42:04       20 阅读
  8. Vue的依赖注入:组件树中的共享数据与功能

    2024-07-22 14:42:04       16 阅读
  9. 常见的坐标系统

    2024-07-22 14:42:04       18 阅读