一、 引言:
最近因为要在symbian系统上移植STL库,因为symbian编译器与windows的编译器的不同,因此产生了诸多代码代码在windows平台下可以编译并运行正常,而在symbian系统上却编译不通过,或产生一定的运行错误,下面根据自己在研发过程中遇到的实际问题进行论述;
二、 问题的阐述
1、 编写C++标准库的auto_ptr产生的问题:
auto_ptr是当前C++标准库中提供的一种智能指针,主要是让资源在局部对象构造时分配,在局部对象析构时释放;
与引用计数型智能指针不同的,auto_ptr要求其对“裸”指针的完全占有性。也就是说一个”裸“指针不能同时被两个以上的auto_ptr所拥有。那么,在拷贝构造或赋值操作时,我们必须作特殊的处理来保证这个特性。auto_ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数,赋值函数不同, auto_ptr的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference).当然,一个auto_ptr也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标对象将先释放其原来所拥有的对象。
正是由于拷贝构造函数和赋值函数的上述设计,导致了下面的代码的设计(除了蓝色部分的代码)的auto_ptr()不能构造右值(标准C++规定,如果一个右值作为函数的参数,这个参数的定义必须是const的,因为右值为常量或临时变量——如函数的返回值);
通过特殊转换auto_ptr_ref
这里提供一个辅助类auto_ptr_ref来做特殊的转换(蓝色部分),
我们本来就可以拷贝和赋值non-const的auto_ptr和禁止拷贝和赋值const的auto_ptr的功能, 只是无法拷贝和赋值临时的auto_ptr(右值), 而这些辅助代码提供某些转换,使我们可以拷贝和赋值临时的auto_ptr,但并没有使const的auto_ptr也能被拷贝和赋值。如下:
auto_ptr<int> ap1 = auto_ptr<int>(new int(0));
auto_ptr<int>(new int(0))是一个临时对象,一个右值,一般的拷贝构造函数当然能拷贝右值,因为其参数类别必须为一个const reference, 但是我们知道,auto_ptr的拷贝函数其参数类型为reference,所以,为了使这行代码能通过,我们引入auto_ptr_ref来实现从右值向左值的转换。其过程为:
1) ap1要通过拷贝 auto_ptr<int>(new int(0))来构造自己
2) auto_ptr<int>(new int(0))作为右值与现有的两个拷贝构造函数参数类型都无法匹配,也无法转换成该种参数类型
3) 发现辅助的拷贝构造函数auto_ptr(auto_ptr_ref<T> rhs) throw()
4) 试图将auto_ptr<int>(new int(0))转换成auto_ptr_ref<T>
5) 发现类型转换函数operator auto_ptr_ref<Y>() throw(), 转换成功,从而拷贝成功。
而通过一个间接类成功的实现了拷贝构造右值(临时对象)
同时,这个辅助方法不会使const auto_ptr被拷贝, 原因是在第5步, 此类型转换函数为non-const的,我们知道,const对象是无法调用non-const成员的, 所以转换失败。当然, 这里有一个问题要注意, 假设你把这些辅助转换的代码注释掉,在vs2008环境下进行编译,该行代码还是可能成功编译,这是为什么呢?debug一下, 我们可以发现只调用了一次构造函数,而拷贝构造函数并没有被调用,原因在于编译器将代码优化掉了。这种类型优化叫做returned value optimization,它可以有效防止一些无意义的临时对象的构造。当然,前提是你的编译器要支持returned value optimization,vs的编译器就支持。
template<class Type>
struct auto_ptr_ref
{
Type* _ref;
explicit auto_ptr_ref(Type* right)
:_ref(right)
{
}
};
template <typename T>
class auto_ptr {
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
public:
inline explicit auto_ptr (pointer p = NULL) : m_p (p) {}
inline auto_ptr (auto_ptr<T>& p) : m_p (p.release()) {}
inline auto_ptr (auto_ptr_ref<T> right )
{
pointer _Ptr = right._ref;
right._ref = 0; // release old
m_p = _Ptr; // reset this
}
template<class _Other>
operator auto_ptr_ref<_Other>()
{
_Other *_Cvtptr = m_p;
auto_ptr_ref<_Other> _Ans(_Cvtptr);
m_p = 0;
return (_Ans);
}
inline ~auto_ptr (void) { delete m_p; }
inline pointer get (void) const { return (m_p); }
inline pointer release (void) { pointer rv (m_p); m_p = NULL; return (rv); }
inline void reset (pointer p = NULL) { if (p != m_p) { delete m_p; m_p = p; } }
inline auto_ptr<T>& operator= (pointer p) { reset (p); return (*this); }
inline auto_ptr<T>& operator= (auto_ptr<T>& p) { reset (p.release()); return (*this); }
inline auto_ptr<T>& operator=(auto_ptr_ref<T> right)
{
pointer Ptr = right._ref;
right._ref = 0; // release old
reset(Ptr); // set new
return (*this);
}
inline reference operator* (void) const { return (*m_p); }
inline pointer operator-> (void) const { return (m_p); }
inline bool operator== (const pointer p) const { return (m_p == p); }
inline bool operator== (const auto_ptr<T>& p) const { return (m_p == p.m_p); }
inline bool operator< (const auto_ptr<T>& p) const { return (p.m_p < m_p); }
private:
pointer m_p;
};
2、 Symbian对模版的嵌套的支持性:
如下代码
template <typename ConstPointer, typename Compare>
int qsort_adapter (const void* p1, const void* p2)
{
ConstPointer i1 = reinterpret_cast<ConstPointer>(p1);
ConstPointer i2 = reinterpret_cast<ConstPointer>(p2);
Compare* comp = (Compare*)g_sort;
return ((*comp) (*i1, *i2) ? -1 : ((*comp) (*i2, *i1) ? 1 : 0));
}
template <typename RandomAccessIterator, typename Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare cmp)
{
g_sort = &cmp;
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
typedef typename iterator_traits<RandomAccessIterator>::const_pointer const_pointer;
qsort (first, distance (first, last), sizeof(value_type), &qsort_adapter<const_pointer, Compare>);
g_sort = NULL;
}
模版函数sort()里面要调用另一个模版函数qsort_adapter();在symbian编译下可能产生编译错误,找不到qsort_adapter()的实例化函数,此种情况在模板类的设计中也产生了问题如下代码
template <typename K, typename V, typename Cmp = less<double>>
class map : public vector<pair<K,V> >
这个模板类的定义里有一个缺省的值,这个缺省值也是一个模板类,如是的定义在symbian的编译环境下进行编译依然是有问题的,或许是symbian的编译器不够只能,所以我们要把嵌套包含的模板类或者模板函数进行类型重定义(typdef),如下:
typedef int (*myfunc)(const void* , const void* );
template <typename RandomAccessIterator, typename Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare cmp)
{
g_sort = &cmp;
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
typedef typename iterator_traits<RandomAccessIterator>::const_pointer const_pointer;
myfunc p = &qsort_adapter<const_pointer, Compare>;
qsort (first, distance (first, last), sizeof(value_type),p);
g_sort = NULL;
}
typedef less<double> less_vdouble;
template <typename K, typename V, typename Cmp = less_vdouble>
这样定义就可以通便编译
3、 对static全局变量的支持
首先symbian系统对static变量和全局变量是支持的,所有的static的用
在symbian模拟器上都可以正常编译及运行,但是在symbian手机上,static全局变量可以正常编译通过,但是运行时就会出现相应的错误(会莫名的程序退出);同过测试已经可以得出,这个异常退出的情况主要出现在,主程序调用静态库函数,这个函数返回一个static变量的指针或者引用时,程序会在symbian手机上出现异常,如下代码:
在lib库中写一个全局函数,返回一个全局的静态变量的指针,
static int* g_test = new int(200);
int* g_func()
{
return g_test;
}
在链接这个lib库的主程序中调用g_func时,在symbian手机上会异常退出;
解决:
把静态全局变量写成局部的静态变量,即上述代码行如下改变
int* g_func()
{
static int* g_test = new int(200);
return g_test;
}