对象的构造和析构

一、初始化和清理的概念(了解)

对象的初始化和清理是两个非常重要的安全问题。c++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

无论你是否喜欢,对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化和清理操作,编译器也会给你增加默认操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。
  • 当对象产生时,必须初始化成员变量;在对象销毁前,必须清理对象

  • 初始化用构造函数,清理用析构函数,这两个函数是编译器调用

二、构造函数和析构函数(重点)

2.1.概念

构造函数和析构函数没有返回值,定义方法是函数名与类名一致

class Maker1()
{
public:
    // 构造函数:作用是初始化成员变量,是编译器去自动调用的
    // 无参构造函数
    Maker1()
    {
        cout << "构造函数" << endl;
    }
    
    // 析构函数:在对象销毁前,编译器调用析构函数
    ~Maker1()
    {
        cout << "析构函数" << endl;
    }
};
​
void test()
{
    Maker1 m;       // 此时会打印“构造函数”
}   // test函数结束时,会打印“析构函数”
class Maker2()
{
public:
    // 构造函数:作用是初始化成员变量,是编译器去自动调用的
    // 有参构造函数
    Maker2(char *p, inr age)
    {
        cout << "构造函数" << endl;
        pName = (char*)malloc(strlen(p) + 1);
        strcpy(pName, p);
        mAge = age;
    }
    
    void printMaker2()
    {
        cout << "name:" << pName << " age:" << mAge << endl;
    }
    
    // 此时构造函数中创造的p堆区空间并未被释放,需要我们定义一个析构函数
    ~Maker2()
    {
        cout << "析构函数" << endl;
        // 释放堆区空间
        if(pName != NULL)
        {
            free(pName)
            pName = NULL;
        }
    }
private:
    char *pName;
    int mAge;
};
​
void test()
{
    Maker2 m;       // 此时会打印“构造函数”
}   // test函数结束时,会打印“析构函数”

2.2. 注意项

有对象产生必然会调用构造函数,有对象销毁必然会调用析构函数

  • 注意1:构造函数和析构函数必须是公有权限

  • 注意2:构造函数可以重载

  • 注意3:构造函数和析构函数没有返回值,不能用void。构造函数可以有参数,析构函数没有参数

  • 注意4:有多少个对象产生就会调用多少次构造函数,有多少个对象销毁就会调用多少次析构函数

2.3.默认的构造和析构

编译器默认提供一个构造函数和析构函数,函数体都为

2.4.拷贝构造函数

1)什么是拷贝构造

拷贝构造函数是在面向对象编程中的一个特殊函数,用于创建一个对象,其内容与另一个已存在的对象相同。通常情况下,拷贝构造函数会在对象被复制时被调用,用于初始化新对象,使其内容与原始对象相同。

拷贝构造函数的作用是创建一个新对象,并将另一个对象的内容复制到新对象中。这在需要不修改原始对象的情况下创建其副本时非常有用。

拷贝构造函数通常用于以下情况:
​
1. 当一个对象作为参数传递给函数,并且需要创建该对象的副本以在函数内部使用时。
2. 当一个对象被赋值给另一个对象时,即对象之间的赋值操作。
3. 当使用对象初始化另一个对象时,如在声明对象时。

class Maker
{
public:
    Maker()
    {
        cout << "无参构造函数" << endl;
        a = 20;
    }
    // 拷贝构造函数
    Maker(const Maker &m)
    {
        cout << "拷贝构造函数" << endl;
        a = m.a
    }
    ~Maker()
    {
    }
    
private:
    int a;
};
​
void test()
{
    Maker m1;
    
    // 用一个已有的对象去初始化另一个对象
    Maker m2(m1);   // 此时会打印“拷贝构造函数”
}
2)编译器提供了默认的拷贝构造函数

默认拷贝构造函数会进行成员变量的简单拷贝

3)拷贝构造函数中的形参要用引用
// 如果拷贝构造函数中的形参不是引用
class Maker
{
public:
    Maker(int a);
    {
        ma = a;
    }
    
    // Maker m4(m1);
    Maker(const Maker m)
    // 由于没有引用,此时编译器会把这串代码转成:const Maker m = m1;
    // 而对于代码const Maker m = m1;编译器会将它看成const Maker m(m1);
    // 于是进行死循环
    {
        cout << "拷贝构造函数" << endl;
    }
};
​
Maker m1(10);           // 调用有参构造函数
Maker m4(m1);           // 调用拷贝构造
Maker m5 = m1;          // 调用拷贝构造

三、构造函数的分类及调用

3.1.分类

  • 按照参数

    • 无参构造函数

    • 有参构造函数

  • 按照类型

    • 普通构造函数

    • 拷贝构造函数

3.2.类默认提供了哪些函数

  • 默认的构造函数

  • 默认的析构函数

  • 默认的拷贝构造函数

  • 默认的赋值函数

3.3.调用

Maker m1(10);           // 调用有参构造函数
Maker m2 = Maker(10);   // 调用有参构造
Maker m3 = 10; --> Maker m3=Maker(10)
​
Maker m4(m1);           // 调用拷贝构造
Maker m5 = m1;          // 调用拷贝构造
​
Maker m6;
m6 = m1;    // 赋值操作

四、匿名对象

4.1.概念

匿名对象是在创建对象时没有指定对象名称的对象。它们通常用于临时的、一次性的操作,不需要在后续代码中引用或操作该对象。

4.2.作用

匿名对象的用途包括但不限于以下几点:

  1. 临时对象:匿名对象可以用于表示临时的中间结果或临时对象,避免创建不必要的变量

  2. 简化代码:在某些情况下,使用匿名对象可以简化代码结构,使代码更加清晰和简洁。

  3. 减少内存开销:由于匿名对象是一次性的,不需要额外的内存空间来存储对象的名称,可以减少内存开销。

4.3.注意

  • 匿名对象的声明周期通常很短,一旦超出作用域就会销毁。

    • 因此在使用时,需要注意避免产生悬空指针未定义行为

void test()
{
    Maker();    // 匿名对象的声明周期在当前行
    Maker(10);
    
    // 注意,如果对象有名字来接,就不是匿名对象
    Maker m1 = Maker();
}

五、拷贝构造函数调用的时机(重点)

class Maker
{
public:
	Maker()
	{
		cout << "无参构造函数" << endl;
	}
	
	Maker(int a)
	{
		ma = a;
		cout << "有参构造函数" << endl;
	}
	
	Maker(const Maker &m)
	{
		cout << "拷贝构造函数" << endl;
	}
	
	~Maker()
	{
		cout << "析构函数" << endl;
	}
};

5.1.对象以值方式传给函数

void func(Maker m){}

void test()
{
	Maker m;
    func(m);	// 会显示”拷贝构造函数“
}

5.2.用一个已有的对象去初始化另一个对象

void test()
{
	Maker m1;
	Maker m2(m1);// 会显示“拷贝构造函数”
}

5.3.函数的局部对象以值的方式从函数返回(需看编译环境)

Maker func()
{
	Maker m;
	cout << "局部对象的地址: " << &m << endl;
	
	return m;
}

void test()
{
	Maker m1 = func();
	
	cout << "m1对象的地址:" << &m1 << endl;
};

输出结果

  • vs Debug模式下

    • 无参数构造函数
      局部对象的地址:0x1
      拷贝构造函数
      析构函数
      m1对象的地址为:0x2(不同)
      析构函数
    • 由于拷贝构造函数的位置是在析构函数的前面,所以 拷贝构造函数 是在“return m”这串代码发生的。

  • vs release模式下(不会调用拷贝构造)

    • 无参数构造函数
      局部对象的地址:0x1
      m1对象的地址:0x1
      析构函数
    • 可以看出来,析构函数不是发生在func函数结束的时候,而是在main函数结束后,而且m1和m的地址相同

    • 也就是说,func函数的m对象内存被保留下来,并赋值给m1

  • qt模式下

    • 同release模式

六、拷贝构造函数的调用规则

  • 1)如果程序员提供了有参构造,那么编译器不会提供默认构造函数(无参),但是会提供默认的拷贝构造

    • class Maker
      {
      public:
      	Maker(int a)
          {
      		
      	}
      };
      
      // Maker m;	// 报错,没有无参构造函数可以接受
      Maker m1(10);
      Maker m(m1);	// 不会报错
  • 2)如果程序员提供了拷贝构造函数,那么编译器不会提供默认的构造函数和默认的拷贝构造函数

七、多个对象的构造函数和析构函数

7.1.构造函数均为无参

1.如果类有成员对象,那么先调用成员对象的构造函数,再调用本身的构造函数

析构函数的调用顺序反之

2.成员对象的构造函数调用和定义顺序一样

3.注意,如果有成员对象,那么实例化对象时,必须保证成员对象的构造和析构能被调用

7.2.其他采用初始化列表

1)初始化列表
  • 在主对象使用,指定调用成员对象的某个构造函数

  • 初始化列表只能写在构造函数

2)注意项
  • 可以使用对象的构造函数传递数值给成员对象的变量

  • 如果使用了初始化列表,那么所有的构造函数都要写初始化列表

    • class Bmw
      {
      	构造函数、析构函数
      };
      
      class Bui
      {
      	构造函数、析构函数
      };
      
      class Maker
      {
      public:
      	// 初始化列表,bmw对象
      	//Maker():bmw(10)
      	//{
      	//
      	//}
      	//另一种形式
      	Maker(int a):bmw(a) 
      	{
      	
      	}
      	
      	Maker(const Maker &m):bmw()
      	{
      	
      	}
      	
      	~Maker()
      	{
      	
      	}
      private:
      	Bmw bmw;
      	Bui bui;
      };

  • 如果有多个对象需要指定调用某个构造函数,用逗号隔开

    • class Maker
      {
      public:
      	// 初始化列表,bmw、bui两个对象
      	Maker(int a, int b. int c):bmw(a), bui(b, c)
      	{
      	
      	}
      	
      	~Maker()
      	{
      	
      	}
      
      private:
      	Bmw bmw;
      	Bui bui;
      };

八、对象的深浅拷贝(重点难点)

8.0.须知

char* p1;
char* p2;
p1 = (char*)malloc(128);
p2 = (char*)malloc(128);
p1 = "小家伙";
strcpy(p2,p1);
这串代码中,p2和p1指向的地址一样吗,存储的地址一样吗,为什么
在这段代码中,p1和p2分别被分配了128个字节的内存空间,然后p1指向了一个字符串常量"小家伙"的地址,p2也指向了另一个128字节的内存空间的地址。在使用strcpy函数将p1指向的字符串复制到p2指向的内存空间时,实际上是将"小家伙"的内容复制到了p2指向的地址中,而不是改变p2本身的指向。

意思就是说,将p2指向的空间的内容修改成p1指向的内容

因此,p2和p1指向的地址是不一样的,存储的地址也是不一样的。

8.1.浅拷贝

就是默认拷贝构造函数;

深浅拷贝是面试的一个经典问题,也是常见的一个坑

8.2.浅拷贝出现的问题

调用默认拷贝函数,可能出现同一块空间被释放两次的情况

class Student
{
public:
	Student(const char *name, int age)
	{
		pName = (char*)malloc(strlen(name) + 1);
		strcpy(pName, name);
		Age = age;
	}
	
	~Student()
	{
		if(pName != NULL)
		{
			free(pName);
			pName =NULL;
		}
	}
private:
	char* pName;
	int Age;
};

void test()
{
	Student s1("笑话", 10);
	Student s2(s1);	// 调用默认拷贝函数,报错:原因是同一块空间被释放两次
}

代码问题

1.第二个pName有没有空间,它存储的内容是不是和pName一样?
2.用strcpy是不是能创造新的空间,来存储原字符串,并使指针指向它?
1.有。是,只不过不是同一个东西,只是内容一样
2.不是。事实上,使用strcpy是将p1指向的内容赋值给p2指向的空间
	所以使用strcpy前,p2必须提取申请空间

8.3.深拷贝解决浅拷贝问题

解决方案就是:自己写拷贝构造函数

Student(const Student &stu)
{
	// 1.申请空间
	pName = (char*)malloc(strlen(stu.pName) + 1);
	
	// 2.拷贝数据
	strcpy(pName, stu.pName);
	age = stu.age;
};

九、explicit关键字的应用(了解)

9.1.概念

explicit是C++中的一个关键字,*它用来修饰只有一个参数的类构造函数,以表明该构造函数是显式的,而非隐式的。*当使用explicit修饰构造函数时,它将*禁止类对象之间的隐式转换,以及禁止隐式调用拷贝构造函数。*

即禁止优化:Maker m = 10;

9.2.为什么需要explicit

explicit关键字的作用是确保类型转换的清晰和明确,避免不必要、可能引发错误的隐式转换。

9.3.使用explicit

在构造函数名前加上explicit

注意:构造函数只能有一个参数或者其他参数都有默认参数

class Maker
{
public:
// 构造函数**只能有一个参数**或者**其他参数都有默认参数**
	explicit Maker(int a)
	{
	
	}
};

十、new和delete申请堆区空间和释放堆区空间(重点)

10.1.为什么不用malloc和free

因为C语言的动态分配内存太复杂,太乱,不好用,而且给类对象指针申请空间时,C语言不会调用构造函数和析构函数

1)程序员必须确定对象的长度
2)malloc返回一个void*指针,C++不允许将void*赋值给其他任何指针,必须强转
3)malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功
4)用户在使用对象之前必须记住对它初始化,构造函数不能显示调用初始化(构造函数是有编译器调用),用户有可能忘记调用初始化函数

10.2.使用

Maker *m1 = new Maker;
Maker *m2 = new Maker(10);	// 可以传参
2)堆区申请和释放数组空间
  • 1.new创建基础类型的数组

int *p = new int[10];

// 注意:如果new有中括号,delete也要加
delete[] p;
  • 2.new创建对象数组

Maker *ms = new Maker[2];//调用无参构造

// 大部分编译器不支持这种写法(聚合初始化)
Maker *m = new Maker[2]{Maker(10), Maker(20)};// 调用有参构造

delete[] ms;
delete[] m;

十一、静态成员(重点)

11.1.概念

使用static关键字 修饰 类的 成员变量 ,那么该成员变量就被声明为“静态成员变量”

11.2.静态成员

11.2.1.静态成员分为:
  • 静态成员变量

    • 1)静态成员变量的生命周期是整个程序,作用域在类内

    • 2)静态成员变量要在类内声明,类外初始化(私有权限的成员也可以初始化)

    • 3)静态成员变量属于类,不属于对象,是所有对象共享

    • 4)静态成员变量可以用类访问,也可以用对象访问

  • 静态成员函数

    • 1)所有成员共享同一个函数

    • 2)静态成员函数只能访问静态成员变量

11.2.2.注意项
  • 静态成员也有权限,如果为私有,类外也不可以访问

  • const修饰的静态成员变量,可以在 类内或类外 初始化(最好在类内初始化)

  • 普通成员函数 可以访问 静态成员变量

十二、c++类对象模型(了解)

12.1.成员变量和函数的存储

  • 1)空类的大小为1

    • 因为编译器可以从内存更好区分对象

    • class Maker{};	// 大小为1
  • 2)类的成员函数静态成员变量静态成员函数不占用;普通成员变量占用类的大小

    • class Maker
      {
      public:
      	void func();
      	static int a;
      	static void func();
      };	// 依旧为1
      int Maker::a = 100;
    • class Maker()
      {
      public:
      	int a;
      };// 类的大小为4
    • 推断出:c++类对象中的变量和函数是 分开存储 的

  • 3)c++编译器对普通成员函数的内部处理(不传入参数却能直接使用成员变量)

12.2.this指针(重点难点)

12.2.1.前言

通过上例,我们得知:c++的数据和操作也是分开存储,并且每一个非内联成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

问题是:这一块代码是如何区分哪个对象调用自己的呢?

c++通过提供特殊的对象指针:this指针,解决上述问题。this指针指向被调用的成员函数**所属的对象**(谁调的,this指针就指向谁)
12.2.2.概念

this指针是隐含在对象的非静态成员函数的一种指针(当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this)

所以,this指针无需定义,直接使用即可

注意:1)静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量
2)this指针的指向不能改变,也就是说this是Maker*const this
12.2.3.用途
  • 1)当形参和成员变量同名时,可用this指针区分

    • class Maker
      {
      public:
      	Maker(int id)
      	{
      		this->id = id;
      	}
      public:
      	int id;
      };
  • 2)在类的非静态成员函数中返回对象本身,可使用return *this(运算符重载时有用)

    • Maker &getMaker()
      {
      	return *this;
      };
12.2.4.拓展
  • this指针指向的空间 没有 存储静态成员变量

  • 需要防止 空指针 调用成员函数

    • class Maker()
      {
      public:
      	void func()
      	{
      		if(this==NULL)
      		{
      			cout<< "this==NULL" << endl;
      			return;
      		}
              cout << a(this->a) << endl;
      	}
      public:
      	a = 20;
      private:
      	int a;
      };
      
      Maker*m = NULL;	// 这里为空时,this指针也会为空,所以可以用来判断(不判断则报错)
      m->func();

12.3.常函数&&常对象

12.3.1.常函数
  • 即用const修饰的成员函数

  • 用const修饰成员函数时,const其实是修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量

  • 当成员变量类型符前用 mutable 修饰时例外

public:
	Maker(int ID, int a)
	{
		this->ID = ID;
		this->a = a;
	}
	void putong()
	{
		this->ID = 10;
		this-a = 100;
	}
	void func()const
	{
		// this->ID = 10;	// 不可以修改
		this-a = 100;		// 在常函数中 可以修改
	}
private:
	int ID;
	mutable int a;
12.3.2.常对象

常对象:在数据类型前面加上const

  • 常对象不能改变普通成员变量的值

  • 常对象不能调用普通成员函数

  • 常对象可以调用常函数(普通对象也可以调用常函数

  • 常对象可以修改mutable修饰的成员

const Maker m(1,18);	// 常对象

// m.ID = 100;	// 常对象不能改变普通成员变量的值
// m.putong();	// 常对象不能调用普通成员函数
m.func();		// 常对象可以调用常函数
m.a = 500;		// 常对象可以修改mutable修饰的成员

十三、友元(重点难点)

13.1.概念

1)友元概念
  • 友元是赋予:全局函数类的成员函数 有访问别的类的私有成员权限。

2)友元函数概念
  • 友元函数是一种特权函数,c++允许这个特权函数访问私有成员

    • 注意:友元函数 不是 类的成员函数

13.2.使用

0.语法
  • friend关键字只出现在声明处

  • 其他类、类成员函数、全局函数都可以声明为友元

  • 友元函数可访问对象任意成员属性,包括私有属性

1.全局友元函数
  • class Building
    {
    	// 声明这个全局函数为Building类的友元函数
    	friend void Good(Building &bd);
    public:
        Building()
        {
    		keting = "客厅";
    		woshi = "卧室";
    	}
    public:
    	string keting;
    private:
    	string woshi;
    };
    
    void Good(Building &bd)
    {
    	cout << bd.woshi << endl;	// 可以访问
    }
2.友元类
  • 1)通过 传入参数 来访问类的私有成员

    • class Building
      {
      	// 声明GoodF类 为Building类的友元类
      	friend class GoodF;
      public:
          Building()
          {
      		keting = "客厅";
      		woshi = "卧室";
      	}
      public:
      	string keting;
      private:
      	string woshi;
      };
      
      class GoodF
      {
      public:
      	void Good(Building &bd)
      	{	// 可以访问
      		cout << bd.woshi << endl;
      	}
      };
  • 2)通过 类内指针 来访问类的私有成员

    • class Building
      {
      	// 声明GoodF类 为Building类的友元类
      	friend class GoodF;
      public:
          Building()
          {
      		keting = "客厅";
      		woshi = "卧室";
      	}
      public:
      	string keting;
      private:
      	string woshi;
      };
      
      class GoodF2
      {
      public:
      	GoodF2()
      	{
      		pbu = new Building;
      	}
      	void Good(Building &bd)
      	{	
      		// 可以访问
      		cout << pbu->woshi << endl;
      	}
      	~GoodF2()
      	{
      		if(pbu != NULL)
      		{
      			delete pbu;
      			pub = NULL;
      		}
      	}
      public:
      	BUilding* pbu;
      };
3.类的友元成员函数(难点)
  • 错误写法(编译器知道类的声明,不知道类的结构)

    • class Building
      {
      	// 声明GoodF类 为Building类的友元类
      	friend void GoodF::func(Building &bud);	// 编译器不知道这个类有没有func函数
      public:
          Building()
          {
      		keting = "客厅";
      		woshi = "卧室";
      	}
      public:
      	string keting;
      private:
      	string woshi;
      };
      
      class GoodF
      {
      public:
      	void func(Building &bd)
      	{	
      		cout << bd.woshi << endl; // 报错,编译器知道类的声明,不知道类的结构
      	}
      };
  • 正确写法

    class Building; // 需要提前声明
    class GoodF		// 需要提前声明
    {
    public:
    	void func(Building &bd);	// 这里需要提前声明Building类
    };
    
    class Building
    {
    	// 声明GoodF类 为Building类的友元类
    	friend void GoodF::func(Building &bud);
    public:
        Building()
        {
    		keting = "客厅";
    		woshi = "卧室";
    	}
    public:
    	string keting;
    private:
    	string woshi;
    };
    
    // 函数实现放到全局
    void GoodF::func(Building &bd)	
    {	
    	cout << bd.woshi << endl;
    }

13.3.注意项

友元类注意

  • 友元关系不能被继承

  • 友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友

  • 友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友

十四、单例模式(重点难点)

14.1.概念

单例模式是指:在内存中只会创建且仅创建一次对象的设计模式。

在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

14.2.实现思路

  • 1)把无参构造函数和拷贝构造函数私有化

  • 2)定义一个类内的静态成员指针

  • 3)在类外初始化时,new一个对象

  • 4)把指针的权限设置为私有,然后提供一个静态成员函数让外面获取这个指针

// 单例模式是一个类只能实例化一个对象
class Maker
{
private:
	// 1.把构造函数和拷贝构造私有化
	Maker()	// 这样就无法直接实例化对象
	{
	}
	Maker(const Maker &m)
	{
	}
public:
	static Maker *getMaker()
	{
	// 第三点也可以在类内申请空间,在类外把它赋值空指针nullptr;
		//if (!pMaker) {
        //    pMaker = new Maker();
        //}
		return pMaker;
	}
private:
// 在私有权限内定义,是因为防止唯一对象指针被赋值为空值
	// 2.定义一个类内的静态成员指针
	static Maker *pMaker;
};

// 3.在类外初始化时,new一个对象
Maker *Maker::pMaker = new Maker;// 这里可以new,是因为有Maker::作用域,编译器这时把它当成类内

void test()
{
	// Maker m; //无法直接实例化对象 
	Maker *m = Maker::getMaker();
	// Maker *m2 = Maker::getMaker();
	// 上面两地址一样
}

面试答题:单例模式
https://blog.csdn.net/weixin_41949328/article/details/107296517?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171411657516800215098961%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171411657516800215098961&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-107296517-null-null.142^v100^pc_search_result_base3&utm_term=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4187

###

Q:在没有对象下,为什么私有成员不是静态成员变量,在类外就不能给它赋值,而静态成员变量就可以
A:私有成员:私有成员是属于类的实例的,每个类的对象都有自己的私有成员变量。私有成员只能在类的成员函数中访问,外部代码无法直接访问或修改私有成员。如果没有类的对象实例,私有成员也不存在,因此在类外部无法给私有成员变量赋值。

静态成员:静态成员是属于类的,而不是类的实例。静态成员变量在类中只有一份拷贝,所有类的对象共享这一份拷贝。静态成员可以在类外部直接访问和修改,因为它属于类而不是类的对象。在没有类的对象实例的情况下,静态成员仍然存在,可以通过类名和作用域解析符号::来访问静态成员。
  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值