Static 用法
详细
1 . 静态变量 – 静态局部变量
(1)该变量在全局数据区分配内存(局部变量在栈区分配内存);
(2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化(局部变量每次函数调用都会被初始化);
(3)静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0(局部变量不会被初始化);
(4)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它(局部变量在栈区,在函数结束后立即释放内存);
这里举个例子:
如果是普通 int a = 1;函数fun每次执行,这里 a = 1;
如果static int a = 10;a–;
第一次执行 a = 10,第二次再来fun, a = 9;
2 . 静态全局变量
(1)静态全局变量不能被其它文件所用
3.静态函数
(1)静态函数不能被其它文件所用;
(2)其它文件中可以定义相同名字的函数,不会发生冲突;
4. 静态成员变量
(1)牵一发而动全身,属于类而不是对象。静态数据成员只分配一次内存
5.静态成员函数
1.静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
2.非静态成员函数可以任意地访问静态成员函数和静态数据成员;
3.静态成员函数不能访问非静态成员函数和非静态数据成员;
4.调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用(因为他本来就是属于类的,用类名调用很正常)
4和5可以通过类名直接调用,class.fun(),不需要通过对象
一个头文件静态变量两个cpp用
模块作用域。static修饰变量的同时进行初始化,限制变量使用范围为定义它的模块(h,cpp),不是整个程序。
如果是静态变量,两个cpp互不干扰。然名称一样,但编译时已是不同的静态变量。
静态变量是定义即初始化,不写自动是 0 。
const
const 修饰成员函数
(1)const 类成员函数中不能修改类的成员变量,const 修饰的是 this 指针指向空间的内容
class Date
{
public:
void GetDay(void);
private:
int m_year;
int m_month;
int m_day;
};
void GetDay(void) const
{
return m_day;
}
(2)如果想要取消常量性 mutable
class Date
{
public:
void GetDay(void);
private:
mutable int m_year;
int m_month;
int m_day;
};
void GetDay(void) const
{
m_year = 2018;
return m_day;
}
https://www.cnblogs.com/loliconinvincible/p/12535515.html
new 和 delete
new的使用(new 和 array new)
new 用于单个对象或者实例的创建,就是调用类的构造函数
new[]用于创建对象或实例的数组实例,并且地址是连续的
释放内存时,new使用 delete,而new[]则对应delete[]。
但是,有些情况使用new[] 和 delete 不会有问题。
char* s = new char[100];
delete s;//没有影响
MyClass* p = new MyClass[10];
delete p;//出问题了
指针指向简单类型,如int、char等,其结果只不过是这块内存被回收,此时使用delete[]与delete没有区别,但如果p指向的是复杂类型,delete[]会针对动态分配得到的每个对象调用析构函数,然后再释放内存。所以第二种,构造了10个,析构了一个,直接bug。
如何知道 指针 指向对象的数组的大小?怎么知道调用几次析构函数?
C++ 的做法是在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete [] 时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。
这也解释了上面内置类型array new 却不使用 array delete的原理。内置类型在 new [ ] 时就没必要多分配那 4 个字节, delete [] 时直接到第二步释放为 int 数组分配的空间。此时跟直接delete是一样的。
new / delete 到底做了什么?
比如 A* a = new A;
三个步骤
- 调用operator new (sizeof(A)) – 分配内存
- 调用构造函数
- 返回指针
delete a;
- 调用 a 指向的对象的析构函数
- 调用operator delete – 释放内存
调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
传入 operator delete[] 函数的参数不是数组对象的指针,是 指针的值减 4。
operator new 和 operator delete
operator new三种形式
throwing (1)
void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2)
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3)
void* operator new (std::size_t size, void* ptr) throw();
(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。
(3)是placement new,它也是对operator new的一个重载。多接收一个ptr参数,但它只是简单地返回ptr。
placement new
比起前两个,只是调用分配内存,对象的地址你是不能决定的。placement new 的好处就是,在用户指定的内存位置(这个指针的位置)上构建新的对象。减少了内存碎片,已分配好的内存上进行对象的构建,构建速度快。
operator delete 两种形式
几乎与 operator new 前两中形式是一样的,但是参数是void*,返回值为void。c++其实有placement delete,但是官方的解释 是 “Does Nothing”。
operator new 的重载
A* p = new A;
如果 A 类重载了 operator new 呢?new 使用的第一步会调用谁?
结论:调用重载的版本,没有就调用全局版本
重载遵循作用域覆盖原则,即在里向外寻找operator new的重载时,只要找到operator new()函数就不再向外查找,哪怕参数不对,不对就直接bug咯。
函数重载本质
(1)特征标不同
返回值算重载吗?
两个同名函数,函数特征标不同,那么就是重载。返回值不同不能算作重载。这个特征标是指:参数个数,参数类型, 参数顺序不同
编译器如何解决重名问题?
编译器神奇的操作 - - 名称修饰,编译器将这重载的函数进行名称的一种加密
在这个修饰里也没有考虑返回值,所以,返回值不一样,编译器以为还是一个函数。
不过返回值不划入范围的好处是,不必联系上下文,确定重载的函数
int func(int a);
double func(int a);
如果这种成立,那么返回值需要提前定义好,编译器还要根据上下文判断返回值类型,再去调用函数。不便独立出来
int a = 6;
int ans1 = func(a);
double ans2 = func(a);
const算重载吗
分情况。
1.
int print(int a)
{
cout<<"int"<<endl;
return a;
}
int print(const int a)
{
cout<<"const"<<endl;
return a;
}
int a 与 const int a 一样
报错。
对于 a 而言,函数调用中存在实参和形参的结合。加入我们用的实参是int a,那么这两个函数都不会改变a的值,这两个函数对于a来说是没有任何区别的,所以不能通过编译,提示重定义。
2 .
void fun(char *a)
{
cout << "non-const fun() " << a;
}
void fun(const char *a)
{
cout << "const fun() " << a;
}
(char *a) 与 (const char *a) 不一样
正确。。因为char *a 中a指向的是一个字符串变量,而const char *a指向的是一个字符串常量,所以当参数为字符串常量时,调用第二个函数,而当函数是字符串变量时,调用第一个函数。
但是注意,非const变量赋值给const合法,第二个版本可以接收const, 但是第一个只能接收非const
3 .
void fun(char *a)
{
cout << "non-const fun() " << a;
}
void fun(char * const a)
{
cout << "const fun() " << a;
}
char *a 与 char * const a一样
报错。两个都是指向字符串变量。
Tips:如int &i 和const int & i 也是可以重载的
引用参数如何算重载?
void func(int & a);
void func(const int & a);
void func(int && a);
是重载。
其中,第一个,参数可以是可修改的左值
第二个是可修改或不可修改的左值,或右值
第三是右值
int x = 5;
const int y = 6;
func(x);// 第一个
func(y);//第二个
func(x + y);//第三个
但是第二个也可接收右值,所以如果第三没有定义,最后一句调用的就是第二个
(2)作用域限定
不载同一作用域,重名函数特征标不同不算重载。
void f(int);
void g()
{
void f(double);
f(1); //这里调用的是f(double),而不是f(int)
}
内层作用域的函数会隐藏外层的同名函数!同样的派生类的成员函数会隐藏基类的同名函数!!
所以派生类中虚函数不写virtual ,依然是虚函数!
(挖坑)
(3)参数匹配
int print(int a);
string print(string a);
那如果我写的是
print(3.0);
如何匹配?
c++ 会实验标准类型转换强制匹配,那么,使用int 的那个。
emplace_back() 与 push_back() 的区别?
底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
#include <vector>
#include <iostream>
using namespace std;
class testDemo
{
public:
testDemo(int num):num(num){
std::cout << "调用构造函数" << endl;
}
testDemo(const testDemo& other) :num(other.num) {
std::cout << "调用拷贝构造函数" << endl;
}
testDemo(testDemo&& other) :num(other.num) {
std::cout << "调用移动构造函数" << endl;
}
private:
int num;
};
int main()
{
cout << "emplace_back:" << endl;
std::vector<testDemo> demo1;
demo1.emplace_back(2);
cout << "push_back:" << endl;
std::vector<testDemo> demo2;
demo2.push_back(2);
}
emplace_back:
调用构造函数
push_back:
调用构造函数
调用移动构造函数
在此基础上,将 testDemo 类中的移动构造函数注释掉,再运行程序会发现,运行结果变为:
emplace_back:
调用构造函数
push_back:
调用构造函数
调用拷贝构造函数
由此可以看出,push_back() 在底层实现时,会优先选择调用移动构造函数,如果没有才会调用拷贝构造函数。