C++学习(五)类和动态内存分配

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<

new和delete缺点:

  1. 如果忘记delete,会引发内存泄漏

  2. 如果在还有指针引用内存的情况下delete,使用指针时就会产生非法内存的错误
    因此,新的标准库提供了智能指针来帮助程序员管理动态内存

    • shared_ptr
    • unique_ptr
    • weak_ptr
      它们都是模板类,定义在memory头文件中
      shared_ptr类
      默认初始化的智能指针中保存一个空指针:

    shared_ptr p1;
    shared_ptr 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对象都有一个关联的计数器,通常称作引用计数器。

  1. 当拷贝一个shared_ptr,计数器就会增加,如
    (1)一个shar_ptr初始化另一个shared_ptr
    (2)作为函数参数
    (3)作为函数返回值
  2. 当我们给shared_ptr赋一个新值或者shared_ptr被销毁时,计数器会递减。
  3. 一旦一个shared_ptr的技术器变成0,它就会自动释放自己所管理的对象。
shared_ptr p=make_shared(10);

make_shared函数在堆上开辟一块动态内存,并将指向该内存的指针包装成智能指针返回给p,这块内存的引用计数为1。(引用计数是对内存而言的)

p是一个局部自动对象,当程序离开块时,p被销毁,引用计数减1。引用计数为0,动态内存被释放。

unique_ptr

一个unique_ptr “独享”它所指向的对象

当unique_ptr被销毁时,它所指向的对象也被销毁

初始化unique_ptr必须采用直接初始化形式

unique_ptr p1;  
unique_ptr p2(new int(42));

weak_ptr

weak_ptr指向由一个shared_ptr管理的对象,但是不会影响它的引用计数

当创建一个weak_ptr时,要用一个shraed_ptr来初始化它

auto p = make_shared(42);  
weak_ptr wp(p);

由于weak_ptr不影响引用计数,它指向的对象可能不存在,所以不能直接使用weak_ptr访问对象

一种安全的做法是使用lock函数,如果weak_ptr指向的对象存在,lock函数返回一个指向共享对象的 shared_ptr

if(shared_ptr 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 
#include 
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<

深拷贝:在构造函数中,为指针类型的成员,分配专门的空间,以这条规则构造的复制,称为深复制。

#include 
#include 
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<

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修饰;

你可能感兴趣的:(C++,C++)