C++学习笔记3:静态与名字空间

why静态
静态数据成员
静态成员函数
静态对象
类作用域及对象的生存期
命名空间

whiy静态

在函数体内定义一个变量时,每次函数调用时编译器会为这些内部变量分配内存, 如果这个变量有一个初始化表达式,那么每当程序运行到初始化表达式中,变量就会被初始化。如果想在两次函数调用之间保留一个变量的值,通常的做法是定义一个全局变量来解决,但是这个变量就不仅仅受这个函数的控制,从某种程度上说存在不安全因素。为了解决这个问题,C和C++允许在函数内部创建一个static对象,这个对象存储在程序的静态数据区,而不是堆栈中。 static对象只在函数第一次调用时初始化依次,以后它将在两次函数操作间保持它的值

静态数据成员

当声明一个类后,可以建立该类的多个对象,每个对象都有类中所定义的数据成员的拷贝,对应不同的存储空间,各个对象相互独立,实现了数据的封装和隐藏;
但在有些情况下,类中的某一数据成员是该类所有对象共有的,需要实现某个数据成员的共享

例如建立一个学生成绩的线性表。设计学生类Student,抽象出的属性:姓名、学号、成绩;用构造函数实现学生的初始化,用成员函数Print实现输出每个学生信息和总人数。
想要统计学生总人数,
一种方法是将需要共享的数据成员定义成全局变量,但是这样做将带来安全隐患,如破坏了类的封装性,不利于信息隐蔽等。
另一种方法是在类中增加一个数据成员来存放总人数,每次用类定义一个对象时,改对象中就会有存放总人数的数据成员的副本。而总人数对于所有对象来说是一样的。因此当总人数发生改变时,所有对象中存放总人数的数据成员都要同时改变,这就需要声明一个专门用来记录变化的总人数的成员函数。这样做很不方便,也容易造成数据的不一致
在C++中提供了静态数据成员来解决数据为对象所共享的问题。类的静态数据成员拥有一块单独的存储区,不管用户创建了该类多少个对象,所有这些对象的静态数据成员都共享这一块静态存储空间,这就为这些对象提供了一种相互通信的方法。
因此,可将总人数定义为静态数据成员

格式👇:

static 类型名 静态成员名;//类内声明
类型 类名::静态数据成员 = 初始化值;// 类外初始化;
#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Student
{
	private:
		char *name;
		int stu_no;
		float score;
		static int total;
	public:
		Student(char *na, int no, float sco);
		void Print();
};
 
int Student::total = 0;
 
Student::Student(char *na, int no, float sco)
{
	name = new char[strlen(na)+1];
	strcpy(name, na);
	stu_no = no;
	score = sco;
	total++;
}
 
void Student::Print()
{
	cout << "NO." << total << "student:" << name << setw(4) << stu_no << setw(4) << score <<endl;
	cout << "Total:" << total <<endl;
}
 
int main()
{
	Student s1("zhangming", 1, 90);
	s1.Print();
	Student s2("wanglan", 2, 95);
	s2.Print();
	Student s3("yumin", 3, 87);
	s3.Print();
	return 0;
}

在数据成员中定义static int total,构造函数中进行操作,每次有对象创建总人数就加1,在类外初始化一次int Student::total = 0;
注意:
1)静态数据成员声明时要加static说明
2)静态数据成员的初始化应在类外声明并在对象生成之前进行,默认初始化为0
3)静态数据成员在编译时创建并初始化。不能用构造函数进行初始化,不能再任何函数内分配存储空间和初始化。
4)静态数据成员属于类,不属于任何一个对象,只能再类外通过类名对它进行访问。静态数据成员的访问形式: 类名::静态数据成员;
5) 静态数据成员的主要用途时定义类的各个对象所公用的数据
6)静态数据成员和普通数据成员一样,可以声明为ppubli、private、protected

例子👇

#include <iostream>
using namespace std;
 
class A
{
	private:
		static int a;
		int b;
	public:
		A(int i, int j);
		void show();
};
 
A::A(int i, int j)
{
	a = i;
	b = j;
}
 
int A::a;
 
void A::show()
{
	cout << "This is static a:" << a <<endl;
	cout << "This is non-static b:" << b <<endl;
}
 
int main()
{
	A x(1, 1);
	x.show();
	A y(2, 2);
	y.show();
	x.show();
	return 0;
}

a被改变

在这里插入图片描述

静态成员函数

创建静态成员函数就是在成员函数名前用static修饰,静态成员函数时为了类的全体服务而不是为了一个类的部分对象服务,因此就不需要定义全局函数。
当产生一个静态成员函数时,也就表达了一个特定类的联系。
静态成员函数不能访问一般的数据成员和成员函数,它只能访问静态数据成员和其他的静态成员函数。静态成员函数是独立于类对象而存在的,因此没有this指针。

static 返回类型 静态成员函数名(参数表);
类名::静态成员函数名(实参表);//调用

例子👇

      #include <iostream>
    #include <iomanip>
    #include <string.h>
    using namespace std;
    class Employee
    {
    	private:
    		char *name;
    		int number;
    		static int total;
    	public:
    		Employee();
    		static void Print();
    		void Printinfo();
    };
     
    int Employee::total = 0;
     
    Employee::Employee()
    {
    	name = new char[10];
    	cout << "Input name and number:" <<endl;
    	cin >> name >> number;
    	total++;
    }
     
    void Employee::Print()
    {
    	cout << endl << "Total:" << total <<endl;
    }
     
    void Employee::Printinfo()
    {
    	cout << "Name:" << name << setw(7) << " " << "Number:" << number <<endl;
    }
     
    int main()
    {
    	Employee::Print();
               int i;
    	Employee s[3];
    	cout <<endl;
    	for(i = 0; i < 3; i++)
    	{
    		s[i].Printinfo();
    	}
    	Employee::Print();
    	return 0;
    }
  

结果:
先调用静态成员函数print,输出静态成员数据3
构造三个对象,三次构造函数
循环输出
调用静态成员函数
在这里插入图片描述1,采用静态成员函数,可以在创建对象之前处理静态数据成员,在不生成类对象的情况下,直接存取静态数据成员,这是普通成员函数不能实现的,因为静态数据是在编译的时候就创建并初始化了的
2,静态成员函数在同一个类中只有一个成员函数的地址映射,节约了计算机系统的开销,提高了程序的运行效率
3,静态成员函数可以在类内定义,也可以在类外定义,在类外定义时,不要用static前缀
参考感谢https://blog.csdn.net/wcybrain/article/details/79048052
在这里插入图片描述
4,静态数据成员可以被非静态成员引用,也可以被静态成员函数引用。但是静态成员函数不能直接访问类中的非静态成员。

==
一般而言,静态成员函数不能访问类中的非静态成员,但是一定要访问的话,静态成员函数只能通过对象名(或指向对象的指针)访问对象的非静态成员

static void Printinfo(Employee);

    void Employee::Printinfo(Employee a)
    {
    	cout <<endl << "Name:" << a.name << setw(7) << " " << "Number:" << a.number <<endl;
    	cout << "Total:" << total <<endl;
    }
     
    int main()
    {
    	int i;
    	Employee s;
    	cout <<endl;
    	Employee::Printinfo(s);
    	return 0;
    }

通过对象s访问非静态成员name和number
如果程序中没有实例化的对象,则只能通过类名::访问静态成员函数;如果有实例化的对象,则既可以通过类名方式访问静态成员函数,也可以通过对象访问静态成员函数。

void A::show()
{
    cout << “This is static a:<< a <<endl;
}
int A::a;
int main
{
    A x(1);
    x.show();     //对象访问静态成员函数
    A::show();    //通过类名访问静态成员函数
    return 0;
}

静态对象

在定义对象时,可以定义类的静态对象。与静态变量一样,在定义对象时,且只是第一次时才需要执行构造函数进行初始化。静态对象的析构函数时在main结束时才自动执行的。与普通对象相同,静态对象的析构函数的执行与构造函数执行的顺序相反

静态对象的定义:static 类名 静态对象名

例子👇:(静态对象的构造函数、析构函数的执行)

#include <iostream>
using namespace std;
 
class Obj
{
	private:
		char ch;
	public:
		Obj(char c);
		~Obj();
};
 
void f();
void g();
Obj A('A');
 
Obj::Obj(char c):ch(c)
{
	cout << "construct..." << ch <<endl;
}
 
Obj::~Obj()
{
	cout << "destruct..." << ch <<endl;
}
 
void f()
{
	static Obj B('B');
}
void g()
{
	static Obj C('C');
}
 
int main()
{
	cout << "inside main()" <<endl;
	f();
	f();
	g();
	cout << "outside main()" <<endl;
	return 0;
}

结果:
对象A是一个全局的obj类的对象,因此在main函数之前就调用A的构造函数。f()函数是定义一个静态对象B,静态对象的定义初始化只执行一次,所以第二次调用f()时就不再执行B的构造了,到g()函数,然后析构函数,与构造函数相反,先执行对象C的析构函数,然后对象B,最后全局对象A
在这里插入图片描述

类作用域及对象的生存期

类作用域

类作用域是指在类的定义中由一对花括号括起来的部分,包括数据成员和函数成员。在类所用域中声明的标识符,其作用域与标识符声明的顺序没有关系。类中的成员函数可以不受限制地访问本类的数据成员和其他成员函数;但是在该类的作用域外,不能直接访问类的数据成员和函数成员,即使是公有成员,也只能通过本类的对象才能访问。

对象的生存期

类对象的生存期是指对象从创建开始到生存期结束的时间

  1. 局部对象:是指定义在一个程序块或函数体内的对象。定义对象时,系统自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象。
  2. 静态对象:作用域从定义开始到程序结束。程序第一次执行静态对象定义时,自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象
  3. 全局对象:作用域是从定义时开始到程序结束。定义对象时,自动调用构造函数创建对象,程序运行结束时调用析构函数释放对象
  4. 动态对象:执行运算符new时创建动态对象,执行delete时释放动态对象

例子👇

#include <iostream>
using namespace std;
 
class Obj
{
	private:
		char ch;
	public:
		Obj(char c);
		~Obj();
};
 
void f();
void g();
Obj A('A');
 
Obj::Obj(char c):ch(c)
{
	cout << "construct..." << ch <<endl;
}
 
Obj::~Obj()
{
	cout << "destruct..." << ch <<endl;
}
 
void f()
{
	static Obj B('B');
        Obj C('C');
}
void g()
{
	static Obj C('C');
}
 
int main()
{
	cout << "inside main()" <<endl;
	f();
	f();
	cout << "outside main()" <<endl;
	return 0;
}

construct……A
inside main()
construct B
construct C
destruct C 立马析构√
construct C只对C构造
destruct C
outside main()
destruct B
destruct A
在这里插入图片描述

命名空间

命名空间是一种将程序库名称封装起来的方法。命名空间是C++为解决变量、函数名和类名等标识符的命名冲突服务的,它将变量等标识符定义在一个不同名的命名空间中。

namespace 命名空间标识符
{
	成员的声明;
}

其中,命名空间标识符名在所定义的域中必须是唯一的,命名空间内的成员既包括变量,也包括函数。命名空间成员的访问方式:命名空间标识符::成员名

在C++标准程序库中,使用了命名空间std。在使用C++标准程序库的任何标识符时,可以直接指定标识符所属的命名空间。

#include <iostream>
using namespace std;
int main()
{
    std::cout << “命名空间” <<endl;
    return 0;
}

应用例子👇

#include <iostream>
#include <string>
#include <string.h>
using namespace std;
 
namespace A
{
	char user_name[] = "namespace A";
	void showname()
	{
		cout << user_name <<endl;
	}
}
 
namespace B
{
	string user_name = "namespace B";
	void showname()
	{
		cout << user_name <<endl;
	}
}
 
int main()
{
	A::showname();
	B::showname();
	strcpy(A::user_name, "good");
	A::showname();
	return 0;
}

在这里插入图片描述

using namespace std;编译指示在本程序中可以使用C++标准库中定义的名字。std是标准命名空间名,在标准头文件中声明的函数、对象和类模板,都在命名空间std中声明。程序中在命名空间A和B中定义了同名的字符串标识符user_name和函数showname(),但是因为是隶属于不同的命名空间,所以同名的字符串标识符user_name和函数showname()互不干扰。在使用命名空间时,可以通过预编译指示使用命名空间。

int main()
{
    using namespace A;
    showname();
    B::showname();
    strcpy(user_name, “good”);
    cout << user_name <<endl;
    return 0;
}

访问命名空间A的变量和函数时可以不显示使用命名空间A的标识符,但是访问命名空间B的变量和函数时,仍需要使用命名空间标识符访问其变量和函数

最后题外:关于赋值,定义两个string类型的变量,并在定义的是偶进行赋值,两个string类型的变量可以用“+”实现连接,并赋值给同类型的变量。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值