您的位置:首页 > 编程语言 > C语言/C++

C++面向对象编程<七>:堆栈、类模板、函数模板及其他补充

2016-02-22 21:19 573 查看

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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: