C++中的静态成员丶友元和内部类

一丶类的静态成员

I. 引入

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

II.特性

  • 静态成员为所有类对象所共享,不属于某个具体的对象,静态成员存在在静态区中。
  • 静态成员变量必须在类外定义,定义时不添加static关键字,类中所写只是声明;即静态成员变量遵循“类内声明,类外定义”的原则。
  • 类静态成员即可用“类名::静态成员"或者“对象.静态成员”两种方式来访问静态成员。
  • 静态成员函数没有隐藏的this指针不能访问任何非静态成员。
  • 静态成员也是类的成员,受public丶protected丶private访问限定符的限制

此外,讲述一下静态成员和非静态成员直接的权限关系。

对于静态成员函数,不能调用非静态成员函数,也不能访问非静态成员变量。
对于非静态成员函数,可以调用静态成员函数,也可以访问静态成员变量。

#include <iostream>

using namespace std;

class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	static int GetACount() 
	{ 
		//Print();		//不行
		//_a = 1;		//不行
		return _scount;
	}
	void Print()
	{
		GetACount();	//可行
		_scount = 0;	//可行
		cout << _scount << endl;
	}

private:
	static int _scount;
	int _a = 0;
};

int A::_scount = 0;

int main()
{
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);
	cout << A::GetACount() << endl;

	a3.Print();
}


二丶友元

友元分为友元函数友元类

I.友元函数

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

现今我们借助友元来实现流插入<<操作符重载流提取>>操作符重载

#include <iostream>

using namespace std;

class Date
{	
	//让这两个重载成为Date类的友元函数  
	//使得这两个函数可以访问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;
	//显式调用
	operator>>(cin, d);

	//隐式调用
	cout << d << endl;
	//显式调用
	operator<<(cout, d) << endl;
	return 0;
}

这里的流插入和流提取重载,都是在类外的全局式重载,再借助friend成为类的友元,使得函数们可以访问类中的所有成员。


那么为什么不让这两个函数在类内作为成员函数进行重载呢?

我们不妨试一下:

#include <iostream>

using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	 
	ostream& operator<<(ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}




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

int main()
{
	Date d1(2024, 4, 16);
	d1 << cout;  //相当于是这样的:d1.operator<<(&d1, cout);
	// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
	//可以看出这是不符合常规调用的

	return 0;
}

这里作为类内成员函数进行重载时,调用的时候会发现不符合常规的调用形式,在形式上反了过来。

这是因为成员函数的参数列表所导致的。

因为成员函数的第一个默认的隐式参数是this,这就导致了在隐式调用重载函数好似,必须要将对象在重载操作符的前面。

在操作符重载那节,我们知道成员函数的特征标(参数列表)中的参数的先后次序,就决定了出现在重载操作符的位置次序。

综上,我们要知道,若想重载流插入和流提取运算符,必须要以全局式重载实现

此时friend关键字的作用也十分清晰,friend关键字用于将普通函数变成类中的友元函数。当成为友元函数后,该函数便可以访问类内的成员们。

注意:friend 函数声明
friend友元函数声明写在类内,它可以写在类内任意的位置,不受权限的影响。一般的话都是放在类内最上面一行。

总结一下友元函数的特性:

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

II.友元类

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

  • 友元关系是单向的,不具有交换性。
  • 友元关系不能传递
  • 友元关系不能继承
#include <iostream>

using namespace std;

class Time
{
	friend class Date;
	//声明我Date类是你Time类的友元
	//那么在Date类中就可以访问Time类的私有成员

	//此时Time类并不能访问Date类的私有成员 注意
	//注意:friend给予的权限是单向的
public:
	Time(int hour = 1, int minute = 1, int second = 1)
		:_hour(hour)
		,_minute(minute)
		,_second(second)
	{
		
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		//直接访问Time类的私有成员
		_t._hour++;
	}

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

private:
	int _year = 1970;
	int _month = 1;
	int _day = 1;

	Time _t;
};

我们在Time类中声明Date类是Time类的友元。

这样使得Date类中所有的成员函数,都成为了Time类的友元函数。

那么在Date类的所有成员函数中,都可以访问Time类的私有成员。

注意:
这里要特别强调一下友元类的特性1:友元关系是单向的,不具有交换性。

我们拿上面的例子来说,此时Date类作为Time类的友元,但是由于只声明了Date类是Time类的友元,并没有在Date类中声明Time类是Date类的友元,所以此时Time类并不是Date类的友元。因为友元关系是单向的权限,不是相互的。


无论是友元函数还是友元类,我们发现通过实现友元,那么原本为private权限的类成员,将可以被直接访问。

从某种程度上讲,这破坏了" 封装 "的特性。因此不建议过多使用友元,除非没有其他方法。


三丶内部类

I. 引入

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

说明:内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员外部类对内部类没有任何优越的访问权限

注意:
内部类就是外部类的友元,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类的所有成员。但是外部类不是内部类的友元。

#include <iostream>

using namespace std;

class A
{
private:
	static int k;
	int h;
public:
	void func()
	{
	//无法访问内部类B的成员
	}

	//内部类B
	//此类为独立的类 只不过放到A里面
	//仅仅受到类域的作用域限制

	//且B天生是A的友元 类B可以访问类A的私有成员
	class B		
	{		
	public:
		void foo(A& a)
	{
		//可以访问外部类A的所有成员
		cout << a.h << endl;
		a.func();
	}
	private:
		int _b;
	};
};


II.特性

内部类的特性:

  • 1.内部类可以定义在外部类的public丶protecte丶private都是可以的,在不同的权限下将影响着内部类是否能通过外部类作用域访问到
  • 2.内部类可以直接访问外部类中的static成员,不需要外部类的对象或类名
  • 3.sizeof(外部类)=外部类本身的大小,和内部类没有关系,外部类和内部类是两两独立的。
#include <iostream>

using namespace std;

class A
{
private:
	static int k;	
	int h = 1;
public:
	void func()
	{
		//无法访问内部类B的成员
	}
	
	class B		
	{	
	public:
		void foo(A& a)
		{
			//可以访问外部类A的所有成员
			cout << a.h << endl;
			//内部类可以直接访问外部类中的static成员,不需要外部类的对象或类名。
			cout << k << endl;
			a.func();
		}
	private:
		int _b = 0;
	};
};

int A::k = 0;

int main()
{
	A a1;
	cout << sizeof(a1) << endl;	//大小为4
	//外被类A和内部类B两两独立
	//在空间存在上也是 

	A::B b1;
	//内部类B处于public权限 
	//因此可以通过类作用域符搜查到
	//B b2;  	//不允许

	return 0;
}

简单说明一下上面的代码:

“sizeof(a1)”;由于内部类A和外部类B的空间独立,此时只是外部类A类被创建了出来,并计算其大小,因此为4;

“A::B b1; ”创建内部类对象,内部类B不可直接创建,比如“B b1”这样;因为类B作为内部类,受作用域限制,它无法在全局被直接检索到,这意味着向“B b1”这样的代码是无法通过的,必须要借助作用域限定符来找到内部类B存在的作用域。
我们知道内部类B在外部类A的作用域中,因此通过外部类A的作用域限定符找到了内部类B的作用域,便可以访问和创建内部类B对象。


本博客仅供个人参考,如有错误请多多包含。
Aruinsches-C++日志-4/20/2024
  • 33
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++程序的运行环境和运行方法 C++程序可以在多种操作系统上运行,包括Windows、Linux、Mac OS等。在Windows操作系统上,可以使用Visual Studio等集成开发环境(IDE)进行开发和运行,也可以使用命令行编译器进行编译和运行。在Linux和Mac OS上,可以使用gcc进行编译和运行。需要注意的是,不同的操作系统和编译器可能会有不同的语法和库文件,需要进行相应的调整。 函数的定义、调用、参数传递和重载 函数是C++程序的基本组成部分,它可以接受参数、执行特定的操作,并返回值。函数的定义包括函数名、参数列表、返回类型和函数体,例如: ``` int add(int a, int b) { return a + b; } ``` 函数的调用可以通过函数名和参数列表来实现,例如: ``` int result = add(3, 4); ``` 参数传递可以通过值传递、指针传递和引用传递来实现。值传递会复制参数的值到函数内部,指针传递会传递参数的地址,引用传递会传递参数的别名。例如: ``` void swap(int& a, int& b) { int temp = a; a = b; b = temp; } int main() { int x = 3, y = 4; swap(x, y); return 0; } ``` 函数重载指的是在同一作用域内定义多个同名函数,但它们的参数列表不同。例如: ``` int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } ``` 类和对象的定义和使用 类是一种自定义的数据类型,它可以包含数据成员成员函数,并且可以进行封装、继承和多态等操作。例如: ``` class Person { public: string name; int age; void sayHello() { cout << "Hello, my name is " << name << ", I am " << age << " years old." << endl; } }; ``` 对象是类的实例化,它可以访问类成员变量和成员函数。例如: ``` Person p; p.name = "Tom"; p.age = 18; p.sayHello(); ``` 构造函数、复制构造函数和组合类 构造函数是用于初始化对象的特殊成员函数,它可以在对象创建时自动调用。例如: ``` class Person { public: string name; int age; Person(string n, int a) { name = n; age = a; } }; ``` 复制构造函数是用于创建对象的副本的特殊成员函数,它会在对象复制时自动调用。例如: ``` Person(const Person& p) { name = p.name; age = p.age; } ``` 组合类指的是一个类包含另一个类的对象。例如: ``` class Student { public: string name; int age; Person p; }; ``` 静态成员元的使用 静态成员是属于类而不是对象的成员,它可以在不创建对象的情况下访问。例如: ``` class Person { public: static int count; Person(string n, int a) { name = n; age = a; count++; } }; int Person::count = 0; ``` 元是一种特殊的关系,它允许一个类的非成员函数访问该类的私有成员。例如: ``` class Person { private: string name; int age; friend void changeName(Person& p, string n); }; void changeName(Person& p, string n) { p.name = n; } ``` 继承与派生 继承是一种面向对象编程的重要概念,它允许创建一个类,该类是已经存在的类的子类。子类可以继承父类的成员变量和成员函数,并且可以添加自己的成员变量和成员函数。例如: ``` class Student : public Person { public: int grade; Student(string n, int a, int g) : Person(n, a) { grade = g; } }; ``` 派生是指从一个类派生出另一个类,即创建一个新类并从已有的类继承属性和方法。例如: ``` class Teacher : public Person { public: vector<Student> students; void addStudent(Student s) { students.push_back(s); } }; ``` 运算符重载 运算符重载可以使得自定义的类的对象可以像内置类型一样使用运算符进行操作。例如: ``` class Vector { public: int x, y; Vector operator+(const Vector& v) { Vector result; result.x = x + v.x; result.y = y + v.y; return result; } }; ``` 多态与虚函数 多态是指同一个函数可以根据不同的对象调用出不同的行为。虚函数是一种特殊的成员函数,它可以被子类重写并且可以根据对象的类型来调用不同的函数。例如: ``` class Shape { public: virtual void draw() { cout << "This is a shape." << endl; } }; class Circle : public Shape { public: void draw() { cout << "This is a circle." << endl; } }; void drawShape(Shape& s) { s.draw(); } ``` 以上就是关于C++语言程序设计的结课论文的一些内容,希望能对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值