C++ 变量也能像python变量一样使用---再也不用担心内存泄露---让malloc/free,new/delete见鬼去吧
仔细想想,其实所有分配在栈和静态存储区的存储空间都是由一个变量来标识,这是由系统自己管理的内存空间,而堆上分配的空间在每次运行期都是不一样的,也就是不是编译期决定的,所以只能用指针来标识。那么我们是否可以将这种指针标识的内存空间标量化,对象化呢?其实是可以的,事实上python就是这么做的。通过采用引用计数的方式来管理这段内存。我们现在要来实现这样的功能。事先声明,我们实现的都是非线程安全的,需要线程安全可以使用互斥锁。
一、dint的实现
1.设计
dint是int的封装,它是这样一种数据类型:1.本质上是int类型,所以我们希望其可以当成int无差别使用,由于int是内置类型,可以隐式转换,所以借助operator()可实现。
2.只有构造函数会真正产生新的资源消耗,其他操作只是将dint邦定到已有的内存空间。
3.dint邦定的内存空间是一段结构体空间,其含有int count(引用计数),int data(数据)。
4.形如a = b;这样的操作发生了如图所示的事件。
2.实现
代码如下:
#include<iostream>
using namespace std;
template<class T>
struct dtype//none thread security
{
int count;
T data;
dtype():count(1){}
dtype(const T& t):count(1),data(t){}
};
class dint{
public:
dint()
{
p = new dtype<int>();
}
dint(const int& i)
{
p = new dtype<int>(i);
}
dint(const dint& d)
{
p = d.p;
p->count++;
}
~dint()
{
p->count--;
if(0 == p->count)
{
destory();
}
}
dint& operator=(dint& d)
{
if(this != &d)
{
int i = --p->count;
if(0 == i)destory();
p = d.p;
p->count++;
}
return *this;
}
operator int&()
{
return p->data;
}
operator int&() const//convert the const dint
{
return p->data;
}
void destory()
{
cout<<"really delete the p."<<endl;
delete p;
}
private:
dtype<int>* p; //p is nerver be NULL,it is allways bind to a int.
};
void test(const dint& i)//covert
{
cout<<i<<endl;
}
void test2(int &h)
{
h = 10000;
}
int main()
{
dint a(10);
dint b(a);
dint c = 100;
c = a;
cout<<a<<endl;
if(a < c)cout<<"Success!"<<endl;
test(90);
int i = 80;
test(i);
test2(a);
cout<<a<<endl;
}
一个解决方案是重载“*“和“->“操作符,那么我们就无法实现python那种调用效果了。但是仍然能够很好的实现内存管理。另一个解决方案是制造一个调用句柄如self()函数。个人比较喜欢self函数。一个解决方案是重载“*“和“->“操作符,那么我们就无法实现python那种调用效果了。但是仍然能够很好的实现内存管理。另一个解决方案是制造一个调用句柄如self()函数。个人比较喜欢self函数。一个解决方案是重载“*“和“->“操作符,那么我们就无法实现python那种调用效果了。但是仍然能够很好的实现内存管理。另一个解决方案是制造一个调用句柄如self()函数。个人比较喜欢self函数。
3.总结
这套方案的好处在于,通过将引用计数与结构体内存邦定,而省去了分配表和分配器的开销。不过同样面临一个问题,那就是线程不安全。如果想要安全,那么必须每一个内存块(被分配的)邦定一个互斥锁。还有些问题:
1. 普通的int(静态+栈):无法真正实现将引用传递给dint类型。
2. dint类型的可以实现传递给int的引用。
3. 也即dint可以完全当作int使用,反之则不行。
4. int ,dint间赋值只是简单的值传递。
二、dstring实现
1.设计
dstring是string的封装,它的描述和dint可以类推:有些不同点在于string的operator()不能发生隐式转换,那么将dstring当作string使用必须显示转换,非常麻烦。
一个解决方案是重载“*“和“->“操作符,那么我们就无法实现python那种调用效果了。但是仍然能够很好的实现内存管理。另一个解决方案是制造一个调用句柄如self()函数。个人比较喜欢self函数。
2.实现
代码:
#include<iostream>
using namespace std;
template<class T>
struct dtype//none thread security
{
int count;
T data;
dtype():count(1){}
dtype(const T& t):count(1),data(t){}
};
class dstring{
public:
dstring()
{
p = new dtype<string>();
}
dstring(const string& i)
{
p = new dtype<string>(i);
}
dstring(const dstring& d)
{
p = d.p;
p->count++;
}
~dstring()
{
p->count--;
if(0 == p->count)
{
destory();
}
}
dstring& operator=(dstring& d)
{
if(this != &d)
{
int i = --p->count;
if(0 == i)destory();
p = d.p;
p->count++;
}
return *this;
}
string& operator*()
{
return p->data;
}
const string& operator*() const
{
return p->data;
}
string& self()
{
return p->data;
}
const string& self() const
{
return p->data;
}
operator string&()
{
return p->data;
}
operator const string&() const//convert the const dstring
{
return p->data;
}
void destory()
{
cout<<"really delete the p."<<endl;
delete p;
}
private:
dtype<string>* p; //p is nerver be NULL,it is allways bind to a string.
};
void test(const dstring& i)//covert
{
cout<<*i<<endl;
cout<<i.self()<<endl;
}
void test2(string &h)
{
h = "10000";
}
int main()
{
dstring a("10");
dstring b(a);
//dstring c = "100";
//c = a;
string str = "abc";
str = a;
cout<<*a<<endl;
test(string("90"));
string i = "80";
test(i);
cout<<(*a).size()<<endl;
test2(*a);
cout<<*a<<endl;
cout<<a.self()<<endl;
}
dstring& operator=(dstring& d)之所以不用常量引用,是为了禁止将常量指针复制给非常量指针,这完全符合C++的
要求,也是出于安全的考虑。三、通用版dT实现
1.设计
对于内置类型,可以通过operator()实现封装类型直接当成基础类型来使用,而类类型的话,我们需要通过重载"*"/"->",或者实现"self()"来达到目的。由于我们此处的设计是值语义的使用封装类型,如果使用"*","->"就会是指针语义,那么这样就更接近于智能指针。我个人倾向于使用self()函数,当然a.self().Fuc()这样的调用也过于冗长,我们需要一个使用起来更简洁的方式,函数U:
template<class T>
typenameT::value_type& U(T& t)
{
return t.self();
}
template <class T>
const typename T::value_type& U(const T& t)
{
return t.self();
}
那么我们就可以这样使用a了:
U(a).Fuc() (Fuc()泛指函数)
还有一个需要注意的地方,由于const类型对象只能调用const版本的函数,那么operator(),"*","->","self()"
2.实现
代码:
#include<iostream>
#include<string>
using namespace std;
template<class T>
struct dtype//none thread security
{
int count;
T data;
dtype():count(1){}
dtype(const T& t):count(1),data(t){}
};
template<class T>
class Obj_ptr{
public:
typedef T value_type;
public:
Obj_ptr()
{
p = new dtype<T>();
}
Obj_ptr(const T& i)
{
p = new dtype<T>(i);
}
Obj_ptr(const Obj_ptr& d)
{
p = d.p;
p->count++;
}
~Obj_ptr()
{
p->count--;
if(0 == p->count)
{
destory();
}
}
Obj_ptr& operator=(Obj_ptr& d)
{
if(this != &d)
{
int i = --p->count;
if(0 == i)destory();
p = d.p;
p->count++;
}
return *this;
}
T& operator*()
{
return p->data;
}
const T& operator*() const
{
return p->data;
}
T& self()
{
return p->data;
}
const T& self() const
{
return p->data;
}
operator T&()
{
return p->data;
}
operator T&() const//convert the const Obj_ptr
{
return p->data;
}
void destory()
{
cout<<"really delete the p."<<endl;
delete p;
}
private:
dtype<T>* p; //p is nerver be NULL,it is allways bind to a T.
};
void test(const Obj_ptr<string>& i)//covert
{
//cout<<i<<endl;
}
void test2(string &h)
{
h = "Hello Test.";
}
template <class T>
typename T::value_type& U(T& t)
{
cout<<"U Function."<<endl;
return t.self();
}
template <class T>
const typename T::value_type& U(const T& t)
{
cout<<"d U Function."<<endl;
return t.self();
}
int main()
{
Obj_ptr<string> a(string("Hello Bosch."));
Obj_ptr<string> b(a);
Obj_ptr<string> c = string("Hello youjin.");
c = a;
cout<<a.self()<<endl;
test(string("Hello China."));
test2(a);
//cout<<a<<endl;
cout<<a.self()<<endl;
cout<<U(a)<<endl;
const Obj_ptr<string> ca(string("Hello Bosch."));
cout<<ca.self()<<endl;
cout<<U(ca)<<endl;
}
该方法的一个特点就是除了构造函数会生成真正意义上的新变量之外,其他操作都只是指针复制等操作。只会改变也有内存的引用计数。
根据C++11的特性我们还能简化创建变量的代码,设置一个创建函数C:
template<class T>
Obj_ptr<T>& C()
{
return Obj_ptr<T>();
}
template<class T>
Obj_ptr<T>& C(const T&t)
{
return Obj_ptr<T>(t);
}
template<class T>
Obj_ptr<T>& C(const Obj_ptr<T> &t)
{
return Obj_ptr<T>(t);
}
调用方式:
1. auto a = C<string>();
2. auto b = C(string("Hello Bosch."));
3. auto c = C(b);
四、总结
对C++11还不是很了解,但是据说非常强大也许等我看完就可以改进该方案了。该方案的思想是把变量视为两类,一类静态+栈空间,一类堆空间。我们要做的是将对空间的内存分配和销毁对用户透明,不鼓励程序员直接参与malloc/free,new/delete的调用。程序员不需要了解内存的分配情况。进一步探讨祥见:C++ 实现Python变量续