指针和引用的区别
引用是C++里面有C里面没有的,它们都能间接地指代其他对象,但引用比指针更安全
指针和引用的区别:
1.存在空指针(不指向任一对象),但不存在空引用(必须初始化),引用总是要指代一个对象(且必须为左值),所以使用引用比指针更高效,指针在使用前得检查其是否有效(为空)。
2.指针可以重新被赋值指向另一个对象,但引用总是指向其初始化指向的对象。
void TEST_REFERENCE() {
int a = 1;
int& b = a;
//int& b; //错误,必须初始化
//int& b = 0; //错误,必须用左值初始化
a = 2;
std::cout << a << b; // 22
int tmp = 3;
b = tmp;
std::cout << a << b; //33
}
使用指针的情况:
1.有可能不指向任何对象,这种情况下可置为空;
2.在不同的情况需要指向不同的对象。
其他情况应使用引用,更安全更高效,总是指向一个对象不变。
未初始化的指针的值是随机的。使用未初始化的指针(野指针)会导致各种问题。所以指针定义时应当初始化,要么指向合法内存(用其他指针赋值或new分配内存),要么赋值为空。即使不指向任何东西,也应该赋值为空指针。在C++11中引入了nullptr作为空指针。
int* p1 = nullptr; // >=C++11
int* p2{}; // 同上
int* p3 = 0; // 不推荐,容易类型混淆
int* p4 = NULL;
指针使用不当导致的问题:
1.野指针。指针未被初始化,或者指针释放后仍去访问那块内存(正常返回但结果错误)。new和delete要对应,并且指针delete释放后应置为空 。
2.内存泄露。已经分配内存的指针未释放就指向其他内存,导致原先分配的内存无法被访问。
3.内存越界。
void TEST1() { // 野指针
int *ptr{new int}; //分配内存
*ptr = 1;
cout<<*ptr<<endl;
//delete ptr;
//cout<<*ptr<<endl; //返回0,但结果错误
//delete ptr;
//ptr = nullptr; //推荐
//cout<<*ptr<<endl; //引发异常,返回非0
}
void TEST2() { // 内存泄露
int* a = new int[5];
a = new int[10]; // 内存泄露,丢失20B
/*
// 正确做法
int* a = new int[5];
delete[]a;
a = nullptr;
a = new int[15];
a[14] = 2;
std::cout << a[14];
*/
}
void TEST3 (){ //
int *a = new int[5]{};
cout<<a[1];
cout<<a[6]; // 越界
}
注意以上代码只为突出问题,并未在意其他细节,例如没有delete。
智能指针
智能指针是C++11的新特性。三种智能指针的定义都在头文件中定义?
智能指针都是类模板,其析构函数用delete释放内存,这也使得智能指针的赋值得是new出来的。
int tmp = 3;
smart_ptr<int>a{&tmp}; //错
int *tmp = new int(3);
smart_ptr<int>a{tmp}; //对
独占指针unique_ptr
1.独占指针存在析构函数,由析构函数释放内存(通过delete),所以独占指针必须赋值(初始化)为动态分配得到的内存。不然普通内存不是new出来的,也没有delete,自然析构函数出错。
2.独占指针不能直接赋值给裸指针。
3.独占指针不能赋值给另一个独占指针,但可以被移动。
总之,独占指针,独占独占,意思就是只有一个指针占用这块内存,不允许其他指针指向这块内存(即不能将独占指针赋值给其他指针,否则就成共享指针了)。但可以通过move语义将独占指针的内存所有权转移至另一独占指针,此时原独占指针为空。
void TEST_UNIQUE_POINTER() {
std::unique_ptr<int>a{ new int };
//std::unique_ptr<int>a = nullptr; //错误
// std::unique_ptr<int>a = new int; //错误
//a = nullptr;
*a = 7;
std::cout << *a << std::endl;
//int tmp = 3;
//std::unique_ptr<int>b{ &tmp }; //编译通过,单步执行该语句也不报错,当b消失时调用其析构函数才报错
int* tmp = new int;
*tmp = 3;
std::unique_ptr<int>b{ tmp };
//int* c = a; //错误。不能直接将独占指针赋值给裸指针
int* c = a.get();
std::cout << *c << std::endl; //正确赋值
//std::unique_ptr<int>d{ a }; //独占指针也不能赋值给独占指针,只能移动
//std::unique_ptr<int>d = a;
std::unique_ptr<int>a1{ move(a) }, a2; //内存所有权转移至a1,a为nullptr
a2 = move(a1); //内存所有权转移至a2,a1为nullptr
if (nullptr==a && nullptr==a1) // true
std::cout << "a=nullptr, a1=nullptr"<<std::endl;
}//函数结束后,a2的析构函数会释放内存
共享指针shared_ptr
多个指针指向同一块内存。当所有shared_ptr不再引用这一内存,内存就被自动释放。
shares_ptr有一个引用计数,代表指向这一内存的共享指针的个数,可用use_count() 获取,当引用计数为0时才delete释放内存,避免了重复释放引发异常。
和unique_ptr相比,shared_ptr可以随意被复制。
推荐使用make_shared创建shared_ptr
void TEST_SHARED_POINTER() {
int* tmp = new int(2);
std::shared_ptr<int>a{ tmp };
//std::shared_ptr<int>a = tmp; 错
std::cout << *a << std::endl; //2
//int tmp = 3;
//std::shared_ptr<int>b{ &tmp }; //错
std::shared_ptr<int>c = a; //随意复制
std::cout << a.use_count() << c.use_count() << std::endl; //22
std::shared_ptr<int>d = std::make_shared<int>(); //初始值为0
std::shared_ptr<int>e = std::make_shared<int>(1); //初始值为1
}//函数结束后,析构函数会释放内存
弱指针weak_ptr
shared_ptr存在循环引用问题,导致内存无法释放。weak_ptr可以打破循环引用。