2018.5.13
在测试策略模式的多对象时,我用到了
vector
的push_back
以及emplace_back
以及list
的push_back
和emplace_back
没想到测试的主要时间花费在了调试程序上!
Bug 程序如下
#include <iostream>
#include <vector>
#include <list>
#define rep( i , j , n ) for ( int i = int(j) ; i < int(n) ; ++i )
#define dew( i , j , n ) for ( int i = int(n-1) ; i > int(j) ; --i )
#define _PATH __FILE__ , __LINE__
typedef std::pair < int , int > P ;
using std::cin ;
using std::cout ;
using std::endl ;
class Strategy {
public:
virtual void Login () = 0 ;
virtual ~Strategy () = default ;
} ;
class Teacher_Login : public Strategy {
public:
void Login () {
cout << endl << "教师登录" << endl ;
}
} ;
class Student_Login : public Strategy {
public:
void Login () {
cout << endl << "学生登录" << endl ;
}
} ;
class Admin_Login : public Strategy {
public:
void Login () {
cout << endl << "管理员登录" << endl ;
}
} ;
class Login_System {
private:
Strategy* strategy ;
public:
explicit Login_System ( Strategy* _strategy = nullptr )
: strategy ( _strategy )
{}
void Call_strategy () {
if ( strategy )
strategy->Login () ;
else
cout << endl << "登录策略为 nullptr\n" ,
cout << __FILE__ << "\t 第 " << __LINE__ << " 行" << endl ;
}
void Change ( Strategy* _strategy = nullptr ) {
if ( strategy != nullptr )
delete strategy ;
strategy = _strategy ;
}
~Login_System () {
if ( strategy != nullptr )
delete strategy ;
strategy = nullptr ;
}
} ;
int main () {
std::vector< Login_System > YHL ;
rep ( i , 0 , 3 )
YHL.emplace_back ( new Teacher_Login () ) ;
rep ( i , 0 , 2 )
YHL.emplace_back ( new Student_Login () ) ;
rep ( i , 0 , 2 )
YHL.emplace_back ( new Admin_Login () ) ;
for ( auto &it : YHL )
it.Call_strategy () ;
YHL.clear () ;
return 0 ;
}
看似正常的程序一直崩溃!
分析
std::vector
+push_back
会崩溃
std::vector
+emplace_back
会崩溃
std::list
+push_back
会崩溃
std::list
+emplace_back
正常 !调试输出,问题出在析构函数几次释放了
strategy
指针
???
为什么std::list
+emplace_back
就正常呢 ?
1.push_back
的参数是value_type
和value_type&&
,如果不是右值引用,那就是拷贝构造函数,而且是浅拷贝
2.emplace_back
是就地构造,不会调用拷贝构造函数,所以std::list
+emplace_back
正常
???
为什么std::vector
+emplace_back
也不行呢?
1. 我没写深拷贝函数,平时经常说浅拷贝,深拷贝,结果防不胜防,以此做警惕
2.std::vector
就是一个顺序表,会经常扩容,调用容器元素的析构函数,即使是emplace_back
也一样会扩容,销毁原来的,重新找块空间
教训
- 对象内部有指针的情况,一定要判断是否需要深拷贝!!!
- 容器内最好不要放实体,应尽量放指针,一来可以减小拷贝赋值的消耗,二来,要注意容器插入
value_type
会自动调用拷贝构造函数。- 尽量少用
std::vector
,一是因为它经常扩容,消耗大;二来它的经常扩容很容易导致指针之间的浅拷贝
修改后的程序
#include <iostream>
#include <typeinfo>
#include <vector>
#include <list>
#define rep( i , j , n ) for ( int i = int(j) ; i < int(n) ; ++i )
#define dew( i , j , n ) for ( int i = int(n-1) ; i > int(j) ; --i )
#define _PATH __FILE__ , __LINE__
typedef std::pair < int , int > P ;
using std::cin ;
using std::cout ;
using std::endl ;
class Strategy {
public:
virtual void Login () = 0 ;
virtual ~Strategy () = default ;
} ;
class Teacher_Login : public Strategy {
public:
void Login () {
cout << endl << "教师登录" << endl ;
}
} ;
class Student_Login : public Strategy {
public:
void Login () {
cout << endl << "学生登录" << endl ;
}
} ;
class Admin_Login : public Strategy {
public:
void Login () {
cout << endl << "管理员登录" << endl ;
}
} ;
class Login_System {
private:
Strategy* strategy ;
public:
explicit Login_System ( Strategy* _strategy = nullptr )
: strategy ( _strategy )
{}
void Call_strategy () {
if ( strategy )
strategy->Login () ;
else
cout << endl << "登录策略为 nullptr\n" ,
cout << __FILE__ << "\t 第 " << __LINE__ << " 行" << endl ;
}
void Change ( Strategy* _strategy = nullptr ) {
if ( strategy != nullptr )
delete strategy ;
strategy = _strategy ;
}
~Login_System () {
if ( strategy != nullptr )
delete strategy ;
strategy = nullptr ;
}
Login_System ( const Login_System& One ) { // 对应的拷贝构造函数
if ( One.strategy != nullptr ) {
if ( typeid ( *One.strategy ) == typeid ( Teacher_Login ) )
this->strategy = new Teacher_Login () ;
else if ( typeid ( *One.strategy ) == typeid ( Student_Login ) )
this->strategy = new Student_Login () ;
else
this->strategy = new Admin_Login () ;
}
}
Login_System ( Login_System&& One ) : strategy ( One.strategy ) {
One.strategy = nullptr ;
}
} ;
int main () {
std::vector< Login_System > YHL ;
rep ( i , 0 , 3 )
YHL.emplace_back ( new Teacher_Login () ) ;
rep ( i , 0 , 2 )
YHL.emplace_back ( new Student_Login () ) ;
rep ( i , 0 , 2 )
YHL.emplace_back ( new Admin_Login () ) ;
for ( auto &it : YHL )
it.Call_strategy () ;
YHL.clear () ;
return 0 ;
}