c++中的static作用
我们都知道c语言中的static的作用有两个:
1. 只初始化一次,而不会不停地初始化,相当于一个小的全局变量
2. 能够将变量的作用域限定在某个源文件中
在c++中的static主要是和类结合到一起的特性,但是它原来在c语言中的特性还是存在的以及c++中类的特性也还是存在的
1. 类中的static成员变量不属于对象,而是属于类的(我们可以使用类来直接操作我们的静态成员函数)
我们来看一个简单的例子,就可以知道了
class Demo
{
public:
static int num_; // 不属于对象的,属于类的
};
Demo::num_ = 100;
从上面的一个小示例中可以看出,我们可以通过类直接操作类中的公有属性的成员变量。
但是,如果我们把权限修改一下,如下所示
class Demo
{
private:
static int num_; // 不属于对象的,属于类的
};
Demo::num_ = 100;
那么此时就不会通过编译了,说明被static修饰后的类的成员变量还是具有原来c++中类的特性的。
再看下面的例子
class Demo
{
public:
int num_; // 不属于对象的,属于类的
};
Demo::num_ = 100;
我们把static去掉,此时编译也是不能通过的,因为num_是属于对象的。
还有一个冷知识,除了static int类型的变量能够在类中声明的时候就能初始化,其它类型的都不能这样做。
// 可以初始化
static const char c = 0;
static const short s = 0;
static const int count = 10;
static const long l = 0;
static const unsigned char uc = 0;
static const unsigned short us = 0;
static const unsigned int ui = 0;
static const unsigned long ul = 0;
// 不可以初始化
const char c = 0;
const short s = 0;
const int count = 10;
const long l = 0;
const unsigned char uc = 0;
const unsigned short us = 0;
const unsigned int ui = 0;
const unsigned long ul = 0;
// 不可以初始化
static const float f = 0.0;
static const double d = 0.0;
其实,总的来说,整型的static const 变量可以直接在类里面初始化,其它的不可以,必须在类的外部初始化。
全局变量和类中的静态成员变量的区别
- 全局的变量到处都能改,而类中的静态成员变量可以告诉别人这个变量是属于哪一个类的,能够避免命名冲突。
- 能够实现封装的功能。
特别需要注意的是静态成员变量的声明方式和初始化方式。
成员变量和成员函数
//.h文件
class Date
{
public:
bool IsYear(int year);
private:
int year_;
};
// .cpp文件
bool Date::IsYear(int year)
{
return year % 100 == 0;
}
此时,我们不可以单独的使用这个Date类中的函数,我们必须实例化一个对象来使用这个函数,但是这样在使用方来说就变得有点复杂了。
如果我们在这个函数前面加上一个static的话,就可以通过类的作用域直接使用这个函数了。这样的成员函数就不再属于对象了,而是属于类了。
//.h文件
class Date
{
public:
static bool IsYear(int year);
private:
int year_;
};
// .cpp文件
bool Date::IsYear(int year)
{
return year % 100 == 0;
}
但是这样又会出现一个问题,我们可以使用两种方式调用这个函数
/.h文件
class Date
{
public:
static bool IsYear(int year);
private:
int year_;
};
// .cpp文件
bool Date::IsYear(int year)
{
return year % 100 == 0;
}
int main()
{
Date demo;
demo.IsYear(2017);
Date::IsYear(2017);
return 0;
}
毫无疑问,这两种方式都可以运行。但是从编程规范来说,使用对象来调用静态成员函数是不对的,因为静态成员函数中是没有this指针的,即没有对象属性。
//.h文件
class Date
{
public:
static bool IsYear(int year);
private:
int year_;
};
// .cpp文件
bool Date::IsYear(int year)
{
this->year_ = 10;
return year % 100 == 0;
}
int main()
{
Date demo;
demo.IsYear(2017);
Date::IsYear(2017);
return 0;
}
会有这样的错误:
error C2355: “this”: 只能在非静态成员函数或非静态数据成员初始值设定项的内部引用,也就意味着不能访问非静态成员变量,它只能访问静态成员变量。
//.h文件
class Date
{
public:
static bool IsYear(int year);
private:
int year_;
static int num_;
};
int Date::num_ = 10;
// .cpp文件
bool Date::IsYear(int year)
{
num_ = 100;
return year % 100 == 0;
}
int main()
{
Date demo;
demo.IsYear(2017);
Date::IsYear(2017);
return 0;
}
费静态成员函数可以访问静态成员变量
//.h文件
class Date
{
public:
static bool IsYear(int year); // 不属于对象,属于类
int GetNum(); // 费静态成员却能够访问静态成员
private:
int year_;
static int num_;
};
int Date::num_ = 10;
// .cpp文件
bool Date::IsYear(int year) // 没有this指针 没有对象属性的
{
// 不能访问非静态成员
num_ = 100;
return year % 100 == 0;
}
int Date::GetNum()
{
return num_;
}
int main()
{
Date demo;
demo.IsYear(2017);
Date::IsYear(2017);
return 0;
}
虽然能够运行,但是严格来说,这样也是不对的,跟上面的错误原因一样。,如果我们要获得一个静态成员变量,那么它就应该是一个静态的成员函数。
//.h文件
class Date
{
public:
static bool IsYear(int year); // 不属于对象,属于类
static int GetNum(); // 费静态成员却能够访问静态成员
private:
int year_;
static int num_;
};
int Date::num_ = 10;
// .cpp文件
bool Date::IsYear(int year) // 没有this指针 没有对象属性的
{
// 不能访问非静态成员
num_ = 100;
return year % 100 == 0;
}
int Date::GetNum()
{
return num_
}
int main()
{
Date demo;
demo.IsYear(2017);
Date::IsYear(2017);
return 0;
}
问题:类里面的静态成员函数和静态成员变量受不受访问权限的控制呢?
答案是肯定的,这是因为:
首先,它是一个类成员
其次,它是类的静态成员,所以受访问权限的控制
关于类中的static特性总结如下:
成员变量:
1. 被所有对象共享的,但是它不属于对象,而是属于类的。(后面的度线程还要被static坑一次)
2. 通过类名直接访问(当然,也可以用对象来访问,虽然这种访问方式在语法上说得过去,但是在编程规范上就不符合了 )
3. 被所有对象共享的,但是它不属于对象,而是属于类的。(后面的度线程还要被static坑一次)
static成员变量是不占用类空间的,可以使用sizeof验证。
成员函数:
成员函数:
1. 没有this指针,意味着无法访问非静态成员(包括变量和函数)
单例模式
// 有的时候,我们只需要一份,比如说计数器
// 单例模式
// 1. 无法初始化
// 2. 能够获取一个对象
// 3.
class Counter
{
public:
//static Counter &GetInstance()
//{
// static Counter demo; // 只会被初始化一次
// return demo;
//}
static Counter *GetInstance()
{
if (!demo)
{
demo = new Counter;
}
return demo;
}
~Counter()
{
std::cout << "~Counter()" << std::endl;
}
private:
Counter()
{
std::cout << "Counter()" << std::endl;
}
Counter(const Counter &){}
Counter &operator=(const Counter &otehr){}
static Counter *demo;
};
Counter *Counter::demo;
int main()
{
//Counter &demo = Counter::GetInstance();
Counter *demo1 = Counter::GetInstance();
Counter *demo2 = Counter::GetInstance();
Counter *demo3 = Counter::GetInstance();
return 0;
}
运行结果如下:
只实例化了一个对象,但是对象始终没有析构,这是因为没有delete,我们再写一个释放对象的函数。
// 有的时候,我们只需要一份,比如说计数器
// 单例模式
// 1. 无法初始化
// 2. 能够获取一个对象
// 3.
class Counter
{
public:
//static Counter &GetInstance()
//{
// static Counter demo; // 只会被初始化一次
// return demo;
//}
static Counter *GetInstance()
{
if (!demo)
{
demo = new Counter;
}
return demo;
}
static void Free()
{
delete demo;
}
~Counter()
{
std::cout << "~Counter()" << std::endl;
}
private:
Counter()
{
std::cout << "Counter()" << std::endl;
}
Counter(const Counter &){}
Counter &operator=(const Counter &otehr){}
static Counter *demo;
};
Counter *Counter::demo;
int main()
{
//Counter &demo = Counter::GetInstance();
Counter *demo1 = Counter::GetInstance();
Counter *demo2 = Counter::GetInstance();
Counter *demo3 = Counter::GetInstance();
Counter::Free();
return 0;
}
运行结果如下:
这样看起来似乎很完美,但是,当我们的工程变大的时候,我们是不知道在哪里释放的,这样会出问题。
我们可以使用内部类和智能指针来实现对象的释放。
但是对于目前来说,还是我们的第一种方法最合适。
C++中的const特性
- const对象只能访问const的函数
- const函数和非const函数可以构成重载
- 我们在写类中的const和非const版本的时候,是有一个约定俗成的规则的,就是非const版本调用const版本。