基础知识
static关键字
内存
静态变量和全局变量一起存储在静态数据段,其中初始化的变量存储在DATA段,未初始化的变量存储在BSS段,程序开始运行时会将BSS段的内容置零。因此可以直接使用未初始化的静态变量(其值为0)。
静态变量初始化时间
全局静态变量应该是在程序运行之前初始化,而C++中,初始化必须执行构造函数才行,所以局部静态变量应该是在第一次实例化对象时初始化。(待求证)
动机
需要某个变量拥有局部变量的作用域但要有全局变量的生命周期,即其不能随着函数的返回而消失,静态变量出现了。
diff
局部静态变量与局部变量:静态变量生命周期变长
全局静态变量与全局变量:静态变量无法被外连接,即其它文件无法通过extern声明获得静态变量的访问权。
静态函数
限制外连接性
静态成员函数
无this指针,可以直接通过类访问(A::foo()),也可以通过对象访问(a.foo()),但是通过对象访问时由于没有传递this指针,因此并不会与具体的对象绑定。因此静态成员函数一般是用来访问静态数据成员的,并不会访问非静态数据成员。还有一个原因是静态成员()会在非静态成员存在之前存在,因此不能访问。在VS2015中键入代码会直接提醒非静态成员变量需要与特定对象绑相对。
优点
数据共享,节省空间
C++和C的区别
C++是面向对象的语言,C是面向过程的结构化语言;
C++具有封装、继承、多态三种特性;
C++支持范式编程,比如模板类,函数模板。
C++中4种cast转换
static_cast
隐式类型转换
int a=1;
double d=static_cast<double>(a);
还可以用于多态向上转换,向下转能成功但是安全性未知(dynamic_cast能执行安全向下转型)
class B{};
class D:public B{void foo(){cout<<D::foo<<endl;}};
int main(){
D* pd=new D;
B* pb=static_cast<B*> pd;
return 0;
}
const_cast
取消常量性
int main() {
int j = 0;
const int i = j;
int &k = const_cast<int &>(i);
++k;
cout << i << endl;
string a = "123";
char *p = const_cast<char *>(a.c_str());
strcpy_s(p,4, "abc");
cout << a.c_str() << endl;
return 0;
}
dynamic_cast
用于多态类型转换,可以完成安全向下转型,只能用于含有虚函数的类,只能转指针或引用。向下转化失败时,对于指针返回nullptr,对于引用抛异常。
reinterpret_cast
重新解释数据
int a=1;
char *p=reinterpret_cast<char *>(a);//error ,int *类型的值不能用于初始化char *类型的实体
int *p=reinterpret_cast<int *>(a);
cout<<"*p"<<endl;
//编译通过,运行时出错,指针访问未知内存
指针和引用
引用的底层实现是指针。用起来的差别:
指针是一个独立的标变量,可以再赋值。引用只是一个变量的别名,初始化后不能再改变
sizeof(指针)=4/8(操作系统地址位数);sizeof(引用)=sizeof(变量)
传指针时还是会产生临时指针变量,传引用则不会
指针可以有多级指针,而引用只有一级
指针和引用使用++运算符的意义不一样
如果返回动态内存分配的对象或内存,必须使用指针,引用可能引起内存泄露(待求证)
指针和数组
指针存放的是地址,指向数据,数组直接存储数据,拥有数据的拷贝
智能指针
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄露。C++11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减1。当引用计数为0时,调用析构函数,释放引用的内存资源。若是循环引用则引用计数无法减到0,造成内存泄露。使用weak_ptr解决这个问题。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。
auto_ptr
C++11已经抛弃
auto_ptr<string> p1(new string("Hellow World!"));
auto_ptr<string> p2;
p2=p1;
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错,此时p1=nullptr.
unique_ptr
unique_ptr<string> p1(new string("Hellow World!"));
unique_ptr<string> p2;
p2=p1;//编译报错
unique_ptr<string> p3;
p3=unique_ptr<string>(new string("You"));//allowed
//move()
p2=move(p1); //allowed
shared_ptr
带引用计数的智能指针
weak_ptr
作用:防止shared_ptr循环引用
它指向一个shared_ptr管理的对象。进行该对象的内存管理的还是那个shared_ptr。weak_ptr只是提供了对被管理对象的一个访问手段,它的构造和析构不会引起引用计数的改变,和shared_ptr可以相互转化,shared_ptr可以直接复制给它,它可以通过调用lock函数来获得shared_ptr。
class B;
class A {
public:
shared_ptr<B> pb_;
~A() {
cout << "delete A" << endl;
}
};
class B {
public:
shared_ptr<A> pa_;
~B() {
cout << "delete B" << endl;
}
};
void foo() {
shared_ptr<B> pb(new B);
shared_ptr<A> pa(new A);
pb->pa_ = pa;
pa->pb_ = pb;
cout << pb.use_count() << endl;
cout << pa.use_count() << endl;
}
int main() {
foo();
return 0;
}
循环引用,函数退出时,AB都没有析构,将其中一个shared_ptr改成weak_ptr即可成功析构。
为什么构造函数不能是虚函数
因为虚函数必须通过虚函数指针找到虚函数表才行,而虚函数指针是在对象的内存空间中,必须调用构造函数构造出对象才有内存空间,死锁了。
1651

被折叠的 条评论
为什么被折叠?



