【C++】类和对象中的static和const

文章目录

      • static
        • static成员变量
        • static成员函数
      • const
      • const static成员变量
      • 对比C语言和C++中的const

static

static成员变量

我们都知道 static 关键字会改变变量的生命周期,其本质是改变变量的存储位置,将变量存放到静态区。

就比如我在函数中声明了一个 static 变量,出了函数作用域这个变量还会存在,当下一次调用函数时直接使用此前定义出的这个变量,而不会再给它开辟一块空间。

那么当用 static 修饰类的成员变量会发生什么呢?

看下面一段代码:

class A {
public:
	void Print() {
		cout << _n << endl;
	}
private:
	int _a;
	static int _n;
};

int main() {
	A aa;
	aa.Print();
	return 0;
}

报错信息如下:

image-20220827131136828

意思就是链接的时候找不到 _n ,换句话说就是 _n 未定义。

这说明了一点,编译器默认生成的构造函数并没有对静态成员变量进行处理,这里我们可以初步推断 _n 不是对象 aa 的成员。

再看一下 aa 的大小:cout << sizeof(aa) << endl;

【C++】类和对象中的static和const_第1张图片

实际上,静态成员变量是所有类对象所共有的,不属于任何一个对象

那静态成员变量怎么定义呢?

在类里面声明一个静态成员变量只是声明,并不是定义。

静态成员变量必须在类外定义,定义时不用添加 static 关键字,但要用限制符说明它是属于哪个类的

class A {
public:
	void Print() {
		cout << _n << endl;
	}
private:
	int _a;
	static int _n;
};

int A::_n = 0;

而如果想要访问静态成员变量,有两种方法。

一来可以像它的定义那样直接通过类名::静态成员变量的形式;

二来静态成员变量属于所有对象共有,所有还可以通过对象名.静态变量或者是this->静态变量的形式访问。

那么静态成员可以做什么呢?

举个简单的例子,假如在一段程序中要统计某个类实例化出来的对象的个数,就可以很简单的通过静态变量来实现:

class A {
public:
	A() { 
        ++_count; 
    }
    
	A(const A& t) { 
        ++_count; 
    }

private:
	static int _count;
};

int A::_count = 0;

因为实例化对象时会调用构造函数或拷贝构造函数,所以每调用一次构造函数或拷贝构造函数就让设置好的计数器 +1 即可,计数器用静态变量来实现。

但是还有一个我问你题,我统计出来了对象的个数,但静态变量是私有的,我得到了也没法访问,这时该怎么做呢?

一种方法是设置一个函数int GetACount(),用匿名对象的方式去访问A().GetACount()

虽然这种方式使用起来要比实例化一个具体的对象然后调用要简单,但也还是会调用一次构造函数和析构函数,多多少少有点消耗。

所以有没有更好的方法呢?


static成员函数

静态成员函数与静态成员变量的区别不大,都是声明的时候加一个 static 关键字修饰,只不过一个是函数,一个是变量。

静态成员必须要在类外面初始化,而静态成员函数就没有初始化这一说法,哪有函数初始化的。

静态成员函数作为成员函数,也是可以被所有类实例化出的对象调用,也可以通过匿名对象调用。

但是,静态成员函数是没有隐藏的 this 指针的

因此,在静态成员函数内部不能直接访问对象的成员变量:

【C++】类和对象中的static和const_第2张图片

但可以访问静态变量:

【C++】类和对象中的static和const_第3张图片

和静态成员变量一样,静态成员函数也可以直接通过以类名::静态成员函数的方式访问:

【C++】类和对象中的static和const_第4张图片

通过这种方式获取到静态变量,没有创造对象,省去了多余的构造和析构。


const

首先复习一下,const 修饰的成员变量具有只读属性,要在构造函数的初始化列表完成初始化。

既然 const 修饰的变量具有了常量属性,那这个变量存放在哪呢?是常量区吗?

这是一个简单的小误区,const 修饰后的变量叫常变量,本质还是变量,只不过只能读不能写,随着对象的销毁它也会销毁,这一点不要和 static 弄混了。

const 修饰成员变量没有什么问题,就一个在初始化列表初始化需要注意的。

那就先看一看下面的代码有什么问题:

class A
{
public:
    A(int a = 0) :_a(a) {}
    
	void Print() {
		cout << _a << endl;
	}
private:
	int _a;
};

int main() {
	const A aa;
	aa.Print();
	return 0;
}

这是 const 对象去调用普通成员函数,程序运行起来:

image-20220829225135086

报错了,忽略这里的报错信息。

发生错误的原因在于 Print 函数的参数是 this 指针,指针权限为可读可写,而调用它的对象是 const 只读类型的,只能读不能写,传过去的指针类型是 const A* this。实参本来是不可修改的,而函数体内却能修改,属于是访问权限放大了,所以会报错。

而解决方法就是将参数 this 指针也变成 const A * 类型,而由于 this 指针作为隐藏参数的特殊性没办法直接加 const,所以C++提供的是这种方法:void Print() const,后面的 const 就用来修饰 this 指针,在函数体内只读不写。

【C++】类和对象中的static和const_第5张图片

对于同一个函数,加 const 和不加 const 修饰是可以构成函数重载的:

class A
{
public:
	void Print() const {
		cout << "void Print() const" << endl;
	}

	void Print() {
		cout << "void Print()" << endl;
	}
};

【C++】类和对象中的static和const_第6张图片

对于只读不写的函数来说,一般要加 const 修饰;

对于需要写的函数来说,不能加 const 修饰;

对于可读可写的函数,例如 operator[]迭代器 iterator 来说,一般要实现加 const 和 不加 const 的两个版本:

【C++】类和对象中的static和const_第7张图片

【C++】类和对象中的static和const_第8张图片

对于 const 的使用,总成一句话就是访问权限不能放大。

const 对象只能调用 const 成员函数,

而非 const 对象既能调用 const 成员函数又能调用非 const 成员函数。

const 成员函数内部也是只能调用其他的 const 成员函数,不能调用非 const

const 成员函数内部则是通吃。


const static成员变量

这是2023.1.12日更新的内容。

上面讲static成员变量时说这种成员变量必须在类外面定义,

这种说法其实是有失偏颇的。

如果是成员变量static const类型呢?

const成员变量可是声明的时候就要初始化啊,

并且不可改变。

所以个人认为这个是C++的一个缺点所在了,

所以static成员变量可以在类里面声明吗?

可以,但仅限于const static


对比C语言和C++中的const

这是2023.1.14更新的内容。

这个问题是在看一道题的时候发现的问题,

此前一直没有注意。

先看下面一段代码:

int main()
{
    const int a = 10;
	int* p = (int*)& a;
    *p = 20;
    printf("a = %d  *p = %d\n", a, *p);
    return 0;
}

下面是在C语言编译器下的运行结果:

image-20230114212724828

下面是在C++编译器下的运行结果:

image-20230114212605702

C语言中的const就不用多说了,

const了,但没完全const,

而C++看似是优化了这一点。

当碰见const声明时在符号表中放入常量

编译过程中若发现使用常量则直接以符号表中的值替换

编译过程中若发现下述情况则给对应的常量分配存储空间

  • 对const常量使用了extern

  • 对const常量使用&操作符

—— 引用自博客【C++基础入门】2.详解C语言和C++中的const_清风自在 流水潺潺的博客-CSDN博客

你可能感兴趣的:(C++,c++,开发语言)