下面小编为大家带来Static成员变量,static成员函数解析(共含8篇),希望能帮助大家!同时,但愿您也能像本文投稿人“zebrabra”一样,积极向本站投稿分享好文章。
最近看Effective C++经常看到一些和static相关的内容,希望综合整理一下,如果有不全,还望补充:
1 类中的Static成员变量
static成员它不像普通的数据成员,static数据成员独立于该类的任意对象而存在,每个static数据成员是与类关联的对象,并不与该类的对象相关联!
一般为类申请一个对象,是对类中成员变量申请一个副本,各个对象之间的成员变量和函数互不影响,但是static成员变量不是在栈空间而是在静态存储区,所有的类对象共享static变量,静态成员是可以独立访问的,无需创建任何对象就可以访问
只是定义一个static变量,这个变量不能在类当中进行初始化,当然包括类的构造函数,static成员的初始化在类的外部进行。
静态数据成员实际上是类域中的全局变量。所以,静态数据成员的定义(初始化)不应该被放在头文件中(在VS中也不报错)。
注:不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif或者#pragma once也不行。
#include “static.h”#include static成员只能被static成员函数访问 static成员可以实施封装,为public,private,protect
注意:const static int 成员可以在类内部进行定义,因为是const成员,除int之外,类中不能声明别的类型的const static成员
const“ int=”int“ okconst=”OKconst“ pre=”pre“ static=”static“ string=”string“>
静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为 所属类类型的 指针或引用。举例如下:
class base{ public : static base _object1;//正确,静态数据成员 base _object2;//错误 base *pObject;//正确,指针 base &mObject;//正确,引用 };
2 类中static成员函数
由于static成员并不属于类中的某一个对象,所以static成员函数没有this指针,一般成员函数可以声明为const说明函数不会修改所属类
的信息,由此可见,没有this指针,静态成员函数不能被声明为const
static成员函数只能够访问static成员不能使用非static成员
class Person { private: string name; const static int age=20; static string address; public: Person(const string&nm):name(nm) {} static void Print() { cout<<”name is “< const int Person::age;//<尽管已经初始化,还是给出定义(C++ primer中规定必须定义,但是在VS中编译运行不定义也没问题)
其中 const static成员函数:这里应该是有一点迷惑性,其实就是返回类型为const
静态成员函数的地址可以赋值给普通的函数指针储存,普通成员函数地址只能赋值给类成员指针
class base{ static int func1(); int func2(); }; int (*pf1)()=&base::func1;//普通的函数指针 int (base::*pf2)()=&base::func2;//成员函数指针3 继承
静态成员变量同样被类的派生类对象共享
#include ”static.h“#include
const” int=“int” pre=“pre” static=“static” string=“string”>
#include
using namespace std;
class Test
{
private:
const int a; //const 成员变量只能在构造函数的成员初始化列表中初始化,不能在函数体中和其他地方
static int b; //static 成员变量需要在全局范围内初始化,格式: 类型名 类名::变量名 = 值
static const int c;//static const 成员变量需要在全局范围内初始化,格式:类型名 类名::变量名 = 值
public:
Test() :a(1){} //正确
//Test(){a = 1;} //错误
int get_a()
{
return a;
}
int get_b()
{
return b;
}
int get_c()
{
return c;
}
};
int Test::b = 2;
const int Test::c = 3;
int main()
{
//int Test::b = 2; 错误
//const int Test::c = 3; 错误
Test temp;
cout << temp.get_a() << endl << temp.get_b() << endl << temp.get_c() << endl;
cin.get();
return 0;
}
除了构造函数、复制构造函数和析构函数外,其他成员函数被用来提供特定的功能,一般来说,提供给外部访问的函数称为接口,访问权限为public,而一些不供外部访问,仅仅作为内部功能实现的函数,访问权限设为private,本节主要讨论函数成员的一些特殊用法。
静态成员函数
成员函数也可以定义成静态的,与静态成员变量一样,系统对每个类只建立一个函数实体,该实体为该类的所有对象共享。
静态成员函数体内不能使用非静态的成员变量和非静态的成员函数。
const与成员函数
第7章已经介绍了const在函数中的应用,实际上,const在类成员函数中还有种特殊的用法,把const关键字放在函数的参数表和函数体之间(与第7章介绍的const放在函数前修饰返回值不同),称为const成员函数,其特点有二:
只能读取类数据成员,而不能修改之
只能调用const成员函数,不能调用非const成员函数
其基本定义格式为:
(1)类内定义时:
类型 函数名(参数列表) const
{
函数体
}
(2)类外定义时,共分两步:
类内声明:
类型 函数名(参数列表) const;
类外定义
类型 类名::函数名(参数列表) const
{
函数体
}
之前工作中遇到一个问题,就像题目中描述的那样,看起来题目有些拗口复杂,这里解释下,当时遇到的需求需要这样处理:调用某个类对象的某个成员函数时,第一次有具体意义的,其他时候都是保持不变的、无意义的,这个需求可以看做是在调用某成员函数时,第一次进行初始化,其他时候不进行操作,即在首次调用时进行初始化,根据这点,很容易想到c/c++里面的static变量,它的作用是保持变量内容的持久,存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。根据需求,使用static局部变量,写下如下代码:
复制代码
1 class T
2 {
3 public:
4 T(const char * pstr):value(pstr){}
5 void Printconst;
6 private:
7 string value;
8 };
9
10 void T::Print()const
11 {
12 static bool bFirstCall = true;
13 if(bFirstCall)
14 {
15 cout<<“first Call ”<
16 bFirstCall = false;
17 }
18 else
19 {
20 cout<<“not first Call ”<
21 }
22 }
即在类的成员函数中定义一个局部的静态变量,使得第一次调用进行初始化,用以区分是否是第一次调用,之后运行else分支。运行下面的测试代码,
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 T t1(“Grubby”);
4 t1.Print();
5 t1.Print();
6
7 T t2(“Moon”);
8 t2.Print();
9 t2.Print();
10
11 return 0;
12 }
通过输出可以看到,t1得到了想要的结果,但是两次t2.Print()都打印 “not first Moon”,这说明此时 bFirstCall 还是false。于是在Print函数中打印bFirstCall地址的代码:
复制代码
1 void T::Print()const
2 {
3 static bool bFirstCall = true;
4 printf(“addr of bFirstCall is %xn”, &bFirstCall);
5 if(bFirstCall)
6 {
7 cout<<“first Call ”<
8 bFirstCall = false;
9 }
10 else
11 {
12 cout<<“not first Call ”<
13 }
14 }
看到四次调用Print函数时打印的bFirstCall的地址相同,这说明类成员函数中的局部静态变量同样属于此函数,而不属于某个对象,不会因为重新定义一个t2对象,第一次调用 t2.Print()时 bFristCall 是 true,不管是哪个T类对象,只在第一次调用 Print()时,bFirstCall == ture,
于是无奈对程序进行了修改,将bFristCall定义为类的静态成员,每次构造函数是将其重置为true:
复制代码
1 class T
2 {
3 public:
4 T(const char * pstr):value(pstr){ bFirstCall = true;}
5 void Print()const;
6 private:
7 string value;
8 static bool bFirstCall;
9 };
10 bool T::bFirstCall;
11
12 void T::Print()const
13 {
14 if(bFirstCall)
15 {
16 cout<<“first Call ”<
17 bFirstCall = false;
18 }
19 else
20 {
21 cout<<“not first Call ”<
22 }
23 }
但是这里又出现了新的问题,即每次构造函数是将bFirstCall重置为true时,如果新定义一个对象,并且没有调用Print()函数,那么再次调用之前定义对象的Print()函数,会产生与预期相反的结果,考虑如下测试代码:
复制代码
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3 T t1(“Grubby”);
4 t1.Print();
5 t1.Print();
6
7 T t2(“Moon”);
8 t1.Print();
9
10 return 0;
11 }
避免这种情况,目前只能保证顺序的对每个对象进行类似Print这样函数的初始化调用,还没有想到好的解决办法。
笔试题(类成员函数)
1.类成员函数的重载、覆盖和隐藏区别?
答案:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无,
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的.函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字,
此时,基类的函数被隐藏(注意别与覆盖混淆)
2. There are two int variables: a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find out the biggest one of the two numbers.
答案:( ( a + b ) + abs( a - b ) ) / 2
3. 如何打印出当前源文件的文件名以及源文件的当前行号?
答案:
cout << __FILE__ ;
cout<<__LINE__ ;
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。
实际工作中模板使用还比较多,而且使用类成员函数或者变量作为模板参数的情况是很多的,在这里先小小举个例子,代码非常简单,但是在实际中确实非常常用,而且实用。
//我只是个测试类而已class TestClass{public: void testFunc(int val){} double testFunc2(double val){return 0;}public: int m_testVal;};//也会用到的写法,在不是模板的情况下还挺实用的typedef void (TestClass::*test_func)(int);//不说boost,不谈lambda,仅仅只提模板而已//成员函数的情况template
编译器默认生成函数的规则如下:
1.定义一个类时,如果自己没有声明,那么编译器会自动帮助生成一个拷贝构造函数(copy construction),赋值操作符(copy assignment),析构函数(deconstruction),
2.如果没有声明任何构造函数(包括拷贝构造函数),编译器会帮助声明一个默认构造函数。
构造函数(包括编译器生成的默认构造函数)的执行包括两个阶段:
1.初始化阶段
2.构造函数体内代码执行构造的阶段
构造函数执行的两个阶段非常重要,在初始化阶段,如果类中存在类类型的成员变量,那么会调用这个类类型的成员变量的默认构造函数来初始化这个成员变量,如果是内置类型,那么可能会对内置类型的变量初始化,也可能不会对其初始化(这点和内置类型的变量是全局变量还是局部变量有关系)。
弄明白了初始化阶段的工作后就明白了构造函数体内执行的代码都是赋值操作而不是初始化操作了。因为初始化操作在构造函数体的代码执行之前就已经完成了。我们也可以对初始化阶段的工作进行控制,这就是C++中的构造函数初始化列表。C++中构造函数初始化列表就是控制初始化阶段的,如果没有提供初始化列表,那么会调用成员变量中的默认构造函数来初始化成员变量。
其实上面的例子已经可以说明问题了,就是Widget类中的Bitmap成员变量在初始化阶段自动调用了默认的构造函数,如果一个类中不存在默认的构造函数(即自己定义了构造函数但是忘记添加默认的构造函数),那么这个类是其他类的成员变量时必须在构造函数初始化列表中提供参数进行初始化,否则会由于在初始化阶段需要调用默认构造函数而却没有而导致构造失败,还是写上一个例子,在上面的类中在加上一个Pane类,注意Widget类的构造函数也做了一些改变:
#include ★ 成员工作计划 ★ 变量与函数说课稿 ★ 成员见面会策划书 ★ 社团成员工作总结 ★ 学生会成员辞职信