1.(new和delete)VS(智能指针)
对象的生命周期:
全局对象:程序启动时分配,程序结束时销毁
局部自动对象:进入块时分配,离开块时销毁
局部static对象:第一次使用时分配,程序结束时销毁
c++中使用new和delete运算符来动态控制内存。
StringBad::StringBad(const char *s)
{
len=std::strlen(s);
str=new char[len+1];
std::strcpy(str,s);
num_string++;
cout<<num_string<<":\" "<<str<<"\"object created\n";
}
类成员str是一个指针,因此使用构造函数必须提供内存来存储字符串。
StringBad::~StringBad()
{
cout<<"\""<<str<<"\"object deleted,";
--num_strings;
cout<<num_string<<"left\n";
delete[] str;
}
当StringBad对象过期时,str指针也将过期。但str影响的内存仍然被分配,除非使用delete将其释放。注意:删除对象可以释放的对象本身占用的内存,但并不能自动的释放属于对象成员的指针指向的内存,因此,需使用析构函数。
new和delete缺点:
-
如果忘记delete,会引发内存泄漏
-
如果在还有指针引用内存的情况下delete,使用指针时就会产生非法内存的错误
因此,新的标准库提供了智能指针来帮助程序员管理动态内存- shared_ptr
- unique_ptr
- weak_ptr
它们都是模板类,定义在memory头文件中
shared_ptr类
默认初始化的智能指针中保存一个空指针:
shared_ptr p1;
shared_ptr<list> p2;
make_shared 函数使用传递参数来构造给定类型的对象shared_ptr p3=make_shared(42);
shared_ptr p4=make_shared(10,‘9’);
shared_ptr p5 = make_shared();//不传递任何参数,对象会进行值初始化。
1.1 为什么使用智能指针
可以认为每个shared_ptr对象都有一个关联的计数器,通常称作引用计数器。
- 当拷贝一个shared_ptr,计数器就会增加,如
(1)一个shar_ptr初始化另一个shared_ptr
(2)作为函数参数
(3)作为函数返回值 - 当我们给shared_ptr赋一个新值或者shared_ptr被销毁时,计数器会递减。
- 一旦一个shared_ptr的技术器变成0,它就会自动释放自己所管理的对象。
shared_ptr<int> p=make_shared<int>(10);
make_shared函数在堆上开辟一块动态内存,并将指向该内存的指针包装成智能指针返回给p,这块内存的引用计数为1。(引用计数是对内存而言的)
p是一个局部自动对象,当程序离开块时,p被销毁,引用计数减1。引用计数为0,动态内存被释放。
unique_ptr
一个unique_ptr “独享”它所指向的对象
当unique_ptr被销毁时,它所指向的对象也被销毁
初始化unique_ptr必须采用直接初始化形式
unique_ptr<double> p1;
unique_ptr<int> p2(new int(42));
weak_ptr
weak_ptr指向由一个shared_ptr管理的对象,但是不会影响它的引用计数
当创建一个weak_ptr时,要用一个shraed_ptr来初始化它
auto p = make_shared<int>(42);
weak_ptr<int> wp(p);
由于weak_ptr不影响引用计数,它指向的对象可能不存在,所以不能直接使用weak_ptr访问对象
一种安全的做法是使用lock函数,如果weak_ptr指向的对象存在,lock函数返回一个指向共享对象的 shared_ptr
if(shared_ptr<int> np = wp.lock())
{
}
2. 复制构造函数
(1)复制构造函数即将一个对象复制到新创建的对象中。用于初始化过程中。
原型:
Class_name (const Class_name &);
如上的
StringBad(const StringBad &);
(2)何时调用复制构造函数?
新建一个对象并将其初始化同类现有的对象时,复制构造函数否将被调用。
假设motto是一个StringBad对象,则下面的4种声明都调用了复制构造函数。
StringBad ditto(motto);
StringBad ditto = motto;
StringBad ditto = StringBad(motto);//使用复制构造函数生成临时对象,然后进行赋值。
StringBad *pStringBad = new StringBad(motto);//声明使用motto初始化一个匿名对象,并将新对象的地址赋给pString指针。
3. 深拷贝vs浅拷贝
浅拷贝:直接为数据成员赋值(将值保存在相应的空间里)
#include <iostream>
#include <cstring>
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s)
{
a=b;
strcpy(str,s); //肇事地点,但不是祸端
}
Test(const Test& C)
{
a=C.a;
strcpy(str,C.str);//肇事地点
}
void show ()
{
cout<<a<<","<<str<<endl;
}
};
int main()
{
Test a(100,"hello");//调用,将hello复制给str,此时的str未经分配地址,是“野指针”
Test b(a);
a.show();
b.show();
return 0;
}
深拷贝:在构造函数中,为指针类型的成员,分配专门的空间,以这条规则构造的复制,称为深复制。
#include <iostream>
#include <cstring>
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s)
{
a=b;
str=new char[strlen(s)+1];
strcpy(str,s); //上一条语句解决肇事地点
}
Test(const Test& C)
{
a=C.a;
str= new char[strlen(s)+1];
strcpy(str,C.str);//解决了肇事地点
}
~Test()
{
delete []str;
}
void show ()
{
cout<<a<<","<<str<<endl;
}
};
int main()
{
Test a(100,"hello");//调用,将hello复制给str,此时的str未经分配地址,是“野指针”
Test b(a);
a.show();
b.show();
return 0;
}
4. static
转载自https://www.cnblogs.com/jhmu063/p/7131997.html
static的作用主要有两种:
第一个作用是限定作用域;第二个作用是保持变量内容持久化;
c语言中static的用法:
1、全局静态变量:
用法:在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。 static int temp;
内存中的位置:静态存储区,在整个程序运行期间一直存在。
初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
2、局部静态变量:
在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。
内存中的位置:静态存储区
初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
3、静态函数:
在函数返回类型前加关键字static,函数就定义成静态函数。函数的定义和生命在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用;
c++中static的用法:
1、类的静态成员:
class A{
private:
static int val;
};
在cpp中必须对他进行初始化,初始化时使用作用域运算符来标明他所属类,其属于该类的所有成员共有,只有一个拷贝;
2、类的静态成员函数:
class A{
private:
static int func(int x);
};
实现的时候也不需要static的修饰,因为static是声明性关键字;类的静态函数是该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,他就是增加了类的访问权限的全局函数;
void A::func(int);
静态成员函数可以继承和覆盖,但无法是虚函数;
3、只在cpp内有效的全局变量:
在cpp文件的全局范围内声明:
static int val = 0;
这个变量的含义是该cpp内有效,但是其他的cpp文件不能访问这个变量;如果有两个cpp文件声明了同名的全局静态变量,那么他们实际上是独立的两个变量;
4、只在cpp内有效的全局函数:
函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;
warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;