注:编码工具是CLion+Cygwin64
目录
智能指针
智能指针可以用于自动回收new方式创建的对象。使用前需要先导入memory头文件。
shared_ptr
#include <iostream>
using namespace std;
#include <memory>
class Test{
public:
~Test(){
cout << "Test析构函数" << endl;
}
};
int main(){
Test * test = new Test;
shared_ptr<Test> ptr(test);
return 0;
}
输出:
Test析构函数
可以看到上面代码中没有调用delete函数回收对象,对象的析构函数也被调用了。
引用计数器
智能指针内部会有一个对象引用计数器,记录对象被智能指针引用的次数。
#include <iostream>
using namespace std;
#include <memory>
class Test{
public:
~Test(){
cout << "Test析构函数" << endl;
}
};
int main(){
Test * test = new Test;
shared_ptr<Test> ptr(test);
cout << "引用计数值为:" << ptr.use_count() << endl;
shared_ptr<Test> ptr2 = ptr;
cout << "引用计数值为:" << ptr.use_count() << endl;
cout << "引用计数值为:" << ptr2.use_count() << endl;
return 0;
}
输出:
引用计数值为:1
引用计数值为:2
引用计数值为:2
Test析构函数
循环引用
shared_ptr类型的智能指针可能导致循环引用,因而对象的析构函数无法被调用。
#include <iostream>
using namespace std;
#include <memory>
class B;
class A{
public:
shared_ptr<B> ptr;
~A(){
cout << "A析构函数" << endl;
}
};
class B{
public:
shared_ptr<A> ptr;
~B(){
cout << "B析构函数" << endl;
}
};
int main(){
A * a = new A;
B * b = new B;
shared_ptr<A> ptrA(a);
shared_ptr<B> ptrB(b);
a->ptr = ptrB;
b->ptr = ptrA;
cout << "A的引用计数为:" << ptrA.use_count() << endl;
cout << "B的引用计数为:" << ptrB.use_count() << endl;
return 0;
}
输出:
A的引用计数为:2
B的引用计数为:2
可以看到并没有输出A和B的析构函数中的打印信息,所以A和B的析构函数没有被调用。这是因为A和B都是通过new方式创建的对象,虽然ptrA和ptrB在main函数执行完被回收了,引用计数器也都减了1,但是A和B中的相互引用还存在,引用计数不为0,智能指针中没有调用delete函数释放A和B。
·要解决循环引用的问题,可以用另一种智能指针weak_ptr。
weak_ptr
weak_ptr类型的智能指针,不会增加对象的引用计数。
#include <iostream>
using namespace std;
#include <memory>
class B;
class A{
public:
weak_ptr<B> ptr;
~A(){
cout << "A析构函数" << endl;
}
};
class B{
public:
weak_ptr<A> ptr;
~B(){
cout << "B析构函数" << endl;
}
};
int main(){
A * a = new A;
B * b = new B;
shared_ptr<A> ptrA(a);
shared_ptr<B> ptrB(b);
a->ptr = ptrB;
b->ptr = ptrA;
cout << "A的引用计数为:" << ptrA.use_count() << endl;
cout << "B的引用计数为:" << ptrB.use_count() << endl;
return 0;
}
输出:
A的引用计数为:1
B的引用计数为:1
B析构函数
A析构函数
unique_ptr
unique_ptr不允许同类型的类型指针赋值。
unique_ptr没有引用计数。
#include <iostream>
using namespace std;
#include <memory>
class Test{
public:
~Test(){
cout << "Test析构函数" << endl;
}
};
int main(){
Test * test = new Test;
unique_ptr<Test> ptr(test);
// unique_ptr<Test> ptr2 = ptr; // 编译不通过
return 0;
}
输出:
Test析构函数
仿写智能指针
#include <iostream>
using namespace std;
template <typename T>
class Ptr{
private:
T * object;
int * count;
public:
Ptr(){
object = NULL;
count = new int(1);
}
Ptr(T * t){
object = t;
count = new int(1);
}
~Ptr(){
if(--(*count) == 0){
if(object)
{
delete object;
}
delete count;
object = NULL;
count = 0;
}
}
// 用一个智能指针对象初始化一个新的智能指针对象时,会调用拷贝构造函数
Ptr(const Ptr<T> & ptr){
cout << "调用了Ptr的拷贝构造函数" << endl;
++(*(ptr.count));
// 如果智能指针之前引用其他对象,则需要先释放
if(--(*count) == 0){
if(object){
delete object;
}
delete count;
}
object = ptr.object;
count = ptr.count;
}
// 先用默认构造函数初始化一个对象,再将另一个对象赋值给此对象时,会用到=
// 所以要重载=运算符
Ptr & operator=(const Ptr & ptr){
cout << "调用了重载=运算符函数" << endl;
++(*(ptr.count));
// 如果智能指针之前引用其他对象,则需要先释放
if(--(*count) == 0){
if(object){
delete object;
}
delete count;
}
object = ptr.object;
count = ptr.count;
return *this;
}
int use_count(){
return *(this->count);
}
};
class Test{
public:
~Test(){
cout << "调用了Test析构函数" << endl;
}
};
int main(){
Test * test = new Test();
Ptr<Test> ptr(test);
cout << "ptr引用计数为:" << ptr.use_count() << endl;
// 调用拷贝构造函数
Ptr<Test> ptr2 = ptr;
cout << endl << "ptr引用计数为:" << ptr.use_count() << endl;
cout << "ptr2引用计数为:" << ptr2.use_count() << endl;
// 调用重载=运算符函数
Ptr<Test> ptr3;
ptr3 = ptr;
cout << endl << "ptr引用计数为:" << ptr.use_count() << endl;
cout << "ptr2引用计数为:" << ptr2.use_count() << endl;
cout << "ptr3引用计数为:" << ptr3.use_count() << endl;
return 0;
}
输出:
ptr引用计数为:1
调用了Ptr的拷贝构造函数
ptr引用计数为:2
ptr2引用计数为:2
调用了重载=运算符函数
ptr引用计数为:3
ptr2引用计数为:3
ptr3引用计数为:3
调用了Test析构函数
类型转换
const_cast
常量转换,可以将常量指针转换为普通指针,从而可以修改该指针所指向的内存地址的值。
#include <iostream>
using namespace std;
class Test{
public:
string name = "default";
};
int main(){
const Test* test = new Test;
cout << "before:name = " << test->name << endl;
// test->name = "update";// 编译不通过
Test * ntest = const_cast<Test*>(test);
ntest->name = "update";
cout << "after:name = " << test->name << endl;
if(test){
delete test;
test = NULL;
}
return 0;
}
输出:
before:name = default
after:name = update
static_cast
静态转换主要是转换指针类型,例如可以将void*转换为int*,double*等,前面线程中就用到了。
静态转换还可以将父类型的对象转换为子类型。
静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。
#include <iostream>
using namespace std;
class Base{
public:
void show(){
cout << "Base show" << endl;
}
};
class Sub: public Base{
public:
void show(){
cout << "Sub show" << endl;
}
};
int main(){
int number = 9999;
void * vp = &number;
int * ip = static_cast<int*>(vp);
cout << "*ip = " << *ip << endl;
Base * base = new Base;
// 静态转换还可以将父类型的对象转换为子类型。
Sub * sub = static_cast<Sub*>(base);
base->show();
// 静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。
sub->show();
if(base){
// 谁是new出来的,就delete谁
delete base;
base = NULL;
}
return 0;
}
输出:
*ip = 9999
Base show
Sub show
dynamic_cast
动态转换运行期才能知道调用哪个函数。
动态转换不能将父类型对象转换为子类型对象。
动态转换返回如果是NULL,表示转换失败。
动态转换中,父类的函数必须声明为虚函数。
#include <iostream>
using namespace std;
class Base{
public:
virtual void show(){
cout << "Base show" << endl;
}
};
class Sub: public Base{
public:
void show(){
cout << "Sub show" << endl;
}
};
int main(){
Base * base = new Base;
// 动态转换不可以将父类型的对象转换为子类型。
Sub * sub = dynamic_cast<Sub*>(base);
if(sub){
cout << "Base->Sub转换成功" << endl;
sub->show();
}else{
cout << "Base->Sub转换失败" << endl;
}
Sub * sub2 = new Sub;
Base * base2 = dynamic_cast<Base*>(sub2);
if(base2)
{
cout << "Sub->Base转换成功" << endl;
base2->show();
}else{
cout << "Sub->Base转换失败" << endl;
}
if(base){
// 谁是new出来的,就delete谁
delete base;
base = NULL;
}
if(sub2){
// 谁是new出来的,就delete谁
delete base;
base = NULL;
}
return 0;
}
输出:
Base->Sub转换失败
Sub->Base转换成功
Sub show
reinterpret_cast
强制转换,静态转换有的功能它都有,同时它还可以将对象转换为数值,可以将数值转换为对象。
可以用于在Java和C++之间传递对象地址,Android源码中Handler和Binder都用到了reinterpret_cast,C++将对象转换为long型值传给Java,C++在需要对象的时候,Java再将long型值传给C++。
#include <iostream>
using namespace std;
class Test{
public:
void show(){
cout << "test reinterpret_cast" << endl;
}
};
int main(){
Test * test = new Test;
long paddr = reinterpret_cast<long>(test);
cout << "paddr = " << paddr << endl;
Test * reTest = reinterpret_cast<Test*>(paddr);
reTest->show();
printf("paddr = %p\n", paddr);
printf("test存储的地址值是:%p\n", test);
printf("reTest存储的地址值是:%p\n", reTest);
if(test){
delete test;
test = NULL;
}
return 0;
}
输出:
paddr = 34360045024
test reinterpret_cast
paddr = 0x80004ade0
test存储的地址值是:0x80004ade0
reTest存储的地址值是:0x80004ade0
宏
#define
代码中使用宏变量,在预处理阶段会将变量替换为值。
#include <iostream>
using namespace std;
#define TAG "HONG"
int main() {
// 预处理阶段,会将TAG替换为"HONG"
cout << TAG << endl;
return 0;
}
输出:
HONG
宏函数
#include <iostream>
using namespace std;
#define SHOW(V) cout << V << endl
#define ADD(A, B) A + B
#define MUL(A, B) A * B
int main() {
SHOW(1);// 预处理后,会替换为cout << 1 << endl;
SHOW("宏函数");// 预处理后,会替换为cout << "宏函数" << endl;
SHOW(3.14);// 预处理后,会替换为cout << 3.14 << endl;
cout << endl;
cout << ADD(1, 2) << endl;// 预处理后,会替换为cout << 1 + 2 << endl;
cout << ADD(1.03, 2) << endl;// 预处理后,会替换为cout << 1.03 + 2 << endl;
cout << endl;
cout << MUL(1, 2) << endl;// 预处理后,会替换为cout << 1 * 2 << endl;
cout << MUL(1 + 1, 2 + 2) << endl;// 预处理后,会替换为cout << 1 + 1 * 2 + 2 << endl;由于是直接替换,不会出现想象中的cout << 1 * 4 << endl;
return 0;
}
输出:
1
宏函数
3.14
3
3.03
2
5
#if、#elif、#else、#endif
#if必须对应#endif,否则报错。
#include <iostream>
using namespace std;
int main() {
#define flag 1
#if flag
cout << "真的" << endl;
#elif !flag
cout << "假的" << endl;
#endif
#if flag
cout << "true" << endl;
#else
cout << "false" << endl;
#endif
return 0;
}
输出:
真的
true
#ifdef、#ifndef、#undef
#include <iostream>
using namespace std;
int main() {
#ifndef CMACRO// 判断是不是没定义宏 CMACRO
#define CMACRO// 只有没定义宏 CMACRO,才会执行此句
#endif
#ifdef CMACRO// 判断是不是定义了宏 CMACRO
cout << "定义了CMACRO" << endl;// 只有定义了宏才会执行此句
#endif
#ifdef CMACRO
#undef CMACRO // 取消定义宏 CMACRO
#endif
#ifdef CMACRO
cout << "定义了CMACRO" << endl;
#else
cout << "没定义CMACRO" << endl;
#endif
return 0;
}
输出:
定义了CMACRO
没定义CMACRO
实践案例
切换测试和线上环境
#include <iostream>
using namespace std;
#ifndef isRelease
#define isRelease 0
#endif
#if isRelease
#define RELEASE
#else
#define DEBUG
#endif
int main() {
#ifdef RELEASE
cout << "是线上环境" << endl;
#else
cout << "是测试环境" << endl;
#endif
return 0;
}
输出:
是测试环境