目录
一、类型转化和智能指针
强制类型转化和智能指针相关, c++对类型转换做了细分,提供了四种不同类型转换,以支持不同需求的转换, 型转换有了统一的标示符,利于代码排查和检视。智能指针最为方便的就是能自动管理堆内存,无需关心何时收回已分配的内存。
二、类型转化
1. static_cast
该转换用于将一种数据类型强制转换为另一种数据类型。在进行数据传递时转void*,即可以使用static_cast找回存在于void*指针中的值。
algo_result.reserved = static_cast<void*>(logName);
char* algo_name = static_cast<char*>(algo_result.reserved);
A: 用于基类和派生类之间指针或引用的转换
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的
B: 用于基本数据类型之间的转换,如把int转换成char。
C: 把任何类型的表达式转换为void类型
2. dynamic_cast
A: 运行时处理的,运行时要进行类型检查。
B: 不能用于内置的基本数据类型的强制转换。
C: 转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。
D: 使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。
class Base
{
public:
Base(int b = 0):_b(b){}
public:
int _b;
};
class Derived:public Base
{
public:
int _c;
int _d;
};
int main(void)
{
int tempA = 1;
int tempB = 2;
Base base;
/*1.无编译告警,但是危险操作,譬如说调用c d会造成不可预知的后果*/
Derived *drvPtrA = static_cast<Derived *>(&base);
drvPtrA->_c = 3;
drvPtrA->_d = 4;
/*2.输出:tempA为5,tempB为4,踩内存了 */
std::cout<<tempA<<std::endl;
std::cout<<tempB<<std::endl;
/*3.Base中没有虚函数,无法查看运行时信息,编译不过*/
//cannot dynamic_cast ‘base’ (of type ‘class Base’) to type ‘class Derived*’ (source is not a pointer)
Derived *drvPtrB = dynamic_cast<Derived *>(base);
return 0;
}
在基类派生类互相转换时,虽然static_cast是在编译期完成,效率更高,但是不安全,上例中就示范了一个踩内存的例子。相比之下因为dynamic_cast可以查看运行时信息,上例如果Base含有虚函数,那么drvPtrB就是一个空指针不能操作Derived中_d的数据从而保证安全性,所以应该优先使用dynamic_cast。
3. const_cast
用于强制去掉不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。
A: 常量指针转换为非常量指针,并且仍然指向原来的对象
B: 常量引用被转换为非常量引用,并且仍然指向原来的对象
C: cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符
const double a = 11;
const double* p = &a;
double* q = const_cast<double*>(p);
4. reinterpret_cast
在使用reinterpret_cast强制转换过程仅仅只是比特位的拷贝,因此在使用过程中需要特别谨慎
class BaseA
{
public:
BaseA(int c = 2):_c(c){}
int _c;
};
class BaseB
{
public:
BaseB(int d = 3):_d(d){}
int _d;
};
int main(void)
{
BaseA baseA;
/*1.编译不过*/
BaseB *baseB = static_cast<BaseB *>(&baseA);
/*2.无任何编译告警,编译通过,正常运行*/
BaseB *baseC = reinterpret_cast<BaseB *>(&baseA);
cout<<baseC->_d<<endl;
return 0;
}
二、智能指针
1. shared_ptr
采用引用计数的方法,记录当前内存资源被多少个智能指针引用,该引用计数的内存在堆上分配。每新增一个引用计数加1,当过期时引用计数减1。只有引用计数为0时,智能指针才会自动释放引用的内存资源。需要用到make函数make_shared()
,shared_ptr
和unique_ptr
的不同之处它在于是可以共享的,它可以赋值给其他shared_ptr
,分享所有权。
int main() {
std::shared_ptr<std::string> Dad{std::make_shared<std::string>("PS4")};
std::shared_ptr<std::string> Son{nullptr};
std::cout << "Dad owns " << (Dad ? *Dad : "nothing") << std::endl;
std::cout << "Son owns " << (Son ? *Son : "nothing") << std::endl;
std::cout << std::endl;
Son = Dad;
std::cout << "Dad owns " << (Dad ? *Dad : "nothing") << std::endl;
std::cout << "Son owns " << (Son ? *Son : "nothing") << std::endl;
std::cout << std::endl;
Dad = nullptr;
std::cout << "Dad owns " << (Dad ? *Dad : "nothing") << std::endl;
std::cout << "Son owns " << (Son ? *Son : "nothing") << endl;
}
Dad owns PS4
Son owns nothing
Dad owns PS4
Son owns PS4
Dad owns nothing
Son owns PS4
2. unique_ptr
对资源独占性的智能指针,一个对象资源只能被一个ptr指向。Dad
对象是指向Son
的unique_ptr
,如果要Dad
放弃对这个对象的所有权,就需要调用Dad.release()
来将所有权进行释放,这个函数将会返回指向Son
的普通指针Son*
,现在就可以用这个不属于任何人的普通指针来构造一个新的unique_ptr
。
int main() {
std::unique_ptr<std::string> Dad{nullptr};
std::cout << "Dad owns " << (Dad ? *Dad : "nothing") << std::endl;
std::unique_ptr<std::string> OldWang{Dad.release()};
std::cout << "Dad owns " << (Dad ? *Dad : "nothing") << std::endl;
std::cout << "OldWang owns " << (OldWang ? *OldWang : "nothing") << std::endl;
}
3. wake_ptr
从shared_ptr构造一个weak_ptr,也就是说有weak_ptr就肯定有shared_ptr,如果要使用weak_ptr所指向的对象,我们需要调用lock()函数,这个函数会返回所指对象的shared_ptr。
shared_ptr<string> AccountOwner{make_shared<string>("Account")};
weak_ptr<string> AccountThief{AccountOwner};
cout << "AccountOwner owns "
<< (AccountOwner ? *AccountOwner : "nothing") << endl;
cout << "AccountThief can use "
<< (!AccountThief.expired() ? *AccountThief.lock() : "nothing") << endl;
cout << endl;
AccountOwner = nullptr;
cout << "AccountOwner owns "
<< (AccountOwner ? *AccountOwner : "nothing") << endl;
cout << "AccountThief can use "
<< (!AccountThief.expired() ? *AccountThief.lock() : "nothing") << endl;
我们不能把并不指向堆内存的指针赋值给智能指针,如delete一个指向栈内存的指针。
4. 注意事项
一个对象的智能指针通过某种路径的一连串智能指针最终指向了自身,最终构成了一个环,这个对象就永远不会被自动释放了,这就是智能指针会造成的内存泄漏。
class foo {
public:
shared_ptr<foo> father;
};
int main() {
shared_ptr<foo> a{make_shared<foo>()};
shared_ptr<foo> b{make_shared<foo>()};
a->father = b;
b->father = a;
}
总结
类型转化和智能指针大法好用