为什么要使用智能指针:
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
auto_ptr 采用所有权形式(一个智能独自占有一个变量),当p2剥夺p1的所有权时,p1失去了所有权,但是不会报错
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud."));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不会报错
unique_ptr 实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。
unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4; //#5
p4 = p3; //此时编译器会报错!!
与此同时,unique_ptr还支持临时右值的赋值
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); // #2 allowed
如果要实现重用智能指针,可以用std::move给指针重新赋值
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
shared_ptr 实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。
use_count 返回引用计数的个数
unique 返回是否是独占所有权( use_count 为 1)
swap 交换两个 shared_ptr 对象(即交换所拥有的对象)
reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的。如 shared_ptr<int> sp(new int(1)); sp 与 sp.get()是等价的
weak_ptr weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
class B;
class A
{
public:
shared_ptr<B> pb_; // change to weak_ptr will solve this dead lock
~A()
{
cout<<"A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete\n";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
// pa and pb will have two ptr referenced
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}
将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。
C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。
用途:
调用函数和做函数的参数,比如回调函数(应用编程和系统编程)。
示例:
char * fun(char * p) {…} // 函数fun
char * (*pf)(char * p); // 函数指针pf
pf = fun; // 函数指针pf指向函数fun
pf(p); // 通过函数指针pf调用函数fun
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销
在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。
const char *arr = "123"; char* brr = "123"; const char crr[] = "123"; char drr[] = "123";
arr位于栈上,”123”位于常量区
brr位于栈上,”123”位于常量区
均位于栈上,但是编译器可能会将”123”优化至常量区
均位于栈区
不会,相当于函数重载
// Example program
#include <iostream>
#include <string>
class Test{
int test1;
int test2;
public:
friend void anything(Test t);
Test(int test1, int test2){
this->test1 = test1;
this->test2 = test2;
}
};
void anything(Test t){
std::cout << "test inside: " << t.test1 << " " << t.test2 << std::endl;
}
int main()
{
anything({1, 2});
}
用于处理无关类型转换,通常为操作数的位模式提供较低层次的重新解释.仅仅解释了比特模型,没有进行二进制转换(下面有例子)
// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>
// Returns a hash code based on an address
unsigned short Hash(void *p) {
unsigned int val = reinterpret_cast<unsigned int>(p);
return (unsigned short)(val ^ (val >> 16));
}
using namespace std;
int main() {
int a[20];
for (int i = 0; i < 20; i++)
cout << Hash(a + i) << endl;
}
在C中,由于C不支持函数重载(overloading), 所以编译器会给每个函数唯一的名字(即函数名),但由于C++支持多态,所以其编译器会为重载的函数生成不同的名字,这样的话会让C编译器看不懂(在把cpp与c的程序链接起来的时候出问题).
extern “C”{} 会保证{}内的函数符号不会mangle(破坏)
In C++, RTTI (Run-time type information) is a mechanism that exposes information about an object’s data type at runtime and is available only for the classes which have at least one virtual function. It allows the type of an object to be determined during program execution
For example, dynamic_cast uses RTTI and following program fails with error “cannot dynamic_cast ‘b’ (of type ‘class B’) to type `class D’ (source type is not polymorphic) ” because there is no virtual function in the base class B.
// CPP program to illustrate
// Run Time Type Identification
#include<iostream>
using namespace std;
class B { };
class D: public B {};
int main()
{
B *b = new D;
D *d = dynamic_cast<D*>(b);
if(d != NULL)
cout<<"works";
else
cout<<"cannot cast B* to D*";
getchar();
return 0;
}
Adding a virtual function to the base class B makes it working.
// CPP program to illustrate
// Run Time Type Identification
#include<iostream>
using namespace std;
class B { virtual void fun() {} };
class D: public B { };
int main()
{
B *b = new D;
D *d = dynamic_cast<D*>(b);
if(d != NULL)
cout << "works";
else
cout << "cannot cast B* to D*";
getchar();
return 0;
}
运行时类型检查,在C++层面主要体现在dynamic_cast和typeid,VS中虚函数表的-1位置存放了指向type_info的指针。对于存在虚函数的类型,typeid和dynamic_cast都会去查询type_info
生成一个临时变量,把它的引用作为函数参数传入函数内。
不能.碰到函数调用需要以值传递的时候,会一直调用拷贝构造函数直至栈溢出
变量类并重载operator()操作符来使类具有函数的性质
it cannot be used for implicit conversions and copy-initialization.
Hosted on GitHub Pages — Theme by orderedlist