stack堆、heap栈
stack:所谓栈就是存在于作用域的一块内存空间,如当你调用函数,函数本身就会形成一个stack用来放置它所接受的参数及local object,以及返回地址;
heap:指操作系统提供的一块global内存空间,程序可能动态分配空间,并取得,并且要自己释放。
如下
class Complex {...};
...
{
Complex c1(1,2); //c1在stack
static Complex c2(1,2);
Complex* p = new Complex(3); //在heap里
}
c1便是所谓的stack object,其声明在作用域就会结束(上面看来就是离开括号),又叫做auto object;而c2便是所谓的static object,其生命在作用域结束之后还在,直到整个程序结束。
如下为全局
class Complex {...};
...
Complex c3(1,2);
int main()
{
...
}
c3便是global object,其生命在整个程序结束之后才结束。其作用域是整个程序。
看一个正确的写法
class Complex {...};
...
{
Complex* p = new Complex;
...
delete p;
}
//p是所谓的heap object,其生命在它被delete后便结束了
看一个错误的写法
class complex {};
...
{
complex *p = new complex;
}
/*
会出现内存泄露,当作用域结束后,p所指的heap object任然存在,但指针p的生命却结束,作用域之外再也看不到p,也就没有机会delete p了
*/
- new、delete
使用如下
Complex* p = new Complex(1,2);
...
delete p;
其中new操作会被转换为下面的步骤
Complex* p;
void* mem = operator new (sizeof(Complex) ); //分配内存
p = static_cast<Complex*>(mem); //类型转换
p->Complex::Complex(1,2); //构造函数
/*
operator new就是一个函数,主要功能分配空间,而构造函数会转换为
Complex::Complex(p,1,2);p就是this指针
*/
其中new的操作步骤示意图如下:
delete的操作会被转换为如下:
Complex::~Complex(p); //析构函数
operator delete(p); //释放内存
再看一个使用如下:
String* p = new String[3];
...
delete[] p; //调用三次析构函数
//delete p; 调用一次析构函数
其中delete的操作步骤示意图如下:
其实new是调用C的malloc函数(分配的空间比申请的空间大),delete函数是调用C的free函数,记住一个new对应一个delete。
类模板、函数模板及其他补充
- static
关系图如下:
类complex成员函数只有一份(不可能创建了几个对象就有几个函数),但是要处理很多对象,那就得靠this pointer来处理不同的对象。
而static的部分就和对象脱离了,它存在于内存的一部分,只有一份。
什么时候会使用static对象呢?就是和对象无关的部分。
如银行会有很多客户对象,但利率却不会和对象有关(大体部分来说)
staic成员函数和一般成员函数的特征就是static成员函数没有this pointer,既然没有this pointer,那static 成员函数不能和一般的函数一样去访问处理non-static data members,那只能处理static members
下面看一个栗子,如下:
class Account
{
public:
static double m_rate;
static void set_rate(const double& x) {m_data = x };
};
double Account::m_rate = 8.0; //静态数据必须要这样定义,因为脱离对象,
int main()
{
Account::set_rate(5.0);
Account a;
a.set_rate(7.0);
}
可以看出调用static函数的两种方式:
1. 通过object调用
2. 通过class name 调用
- 单例模式
class A
{
public:
static A& getInstance();
setup() {...}
private:
A();
A(const A& rhs);
static A a;
...
};
A& A::getInstance()
{
return a;
}
a本来就存在,和对象无关,然后不想其他人创建,那就把构造函数放在private里,那怎么取得a呢,就用个static A& getInstance()取得a,这是与外界的接口。但这不是最好的写法,因为不管你用不用,a都存在。所以更好的写法如下
class A
{
public:
static A& getInstance();
setup() {...}
private:
A();
A(const A& rhs);
...
};
A& A::getInstance()
{
static A a; //只有当有人掉用这个函数,a才会存在
return a;
}
cout
为什么cout能接受不同类型的参数呢?如下图
可以看出cout就是一种ostream,
可以看出cout实际上是重载了<<运算符的函数,用于打印不同类型的数template
分为两种:class template 和 function template
class template见下程序
template<typename T>
class complex
{
public:
complex (T r = 0, T i = 0)
: re (r), im(i)
{}
complex& operator += (const complex&);
T real() const {return re; }
T imag() const {return im; }
private:
T re, im;
friend complex& _doapl (complex*, const complex&);
};
//调用如下
{
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
}
function template见下程序
class stone
{
public:
stone(int w, int h, int we)
: _w(w), _h(h), _weight(we)
{}
bool operator < (const stone& rhs) const
{ return _weight < rhs._weight; }
private:
int _w, _h, _weight;
};
template<class T>
inline const T& min(const T& a, const T& b)
{
return b < a ? b : a;
}
//使用
stone r1(2,3), r2(3,3), r3;
r3 = min(r1,r2); //则会调用min函数,函数里面会接着调用stone::operator<函数
- namespace
以防写的东西与别人同名。使用方法有两种,见下两种:
using directive:
using namespace std; //把namspace空间的东西全打开
cin >> i;
cout << "hello" << endl;
using declaration:
using std::cout;
std::cin >> i;
cout << "hello" << endl;