??xml version="1.0" encoding="utf-8" standalone="yes"?>
模拟它的MVO架构Q不q没有原代码Q所以很难a
设计了一个交互绘囑֟本类Q?br />但是q有错误Q鼠标左键点M?br /> winGDI.cpp中出错?br />请大虾指教一二?br />我已l在q个问题上花了很多的心思?br />其中最重要的就是Painter抽象cȝ设计
他的子类QBufferDCl承CDC
而SGView包含Painter指针Q方便在SGView ::drawEntity调用?br />
SGObject - 对象的抽象类Q也是几何对象
SGModel- 负责对象理Q没有实现所有的功能Q准备用SceneTree来实?br />SGView - 负责昄SGModel中的数据Q关键的函数
void SGView::drawEntity(SGObject* pObj)
{
pObj->draw(m_pPainter,this)
}
Painter - 装CDC的功?见代?br />
在CSGView创徏的时候创建Painter对象
很可能这里有问题Q!Q!Q?br />void CSGView::OnCreate(..)
{
CDC* pDC = GetDC();
Painter* painter = new QBufferDC(pDC);
m_pSGView->setPainter(painter);
}
MFC 相关的Document/View架构
CSGDocument - 理SGModel
CSGView - 和SGView建立联系Qƈ负责把windows的消息发送给SGView
见原代码
SGActionManager - 负责工具的管?br />SGBaseAction - 工具的抽象基c?br />SGActionDrawLine - l制直线的工?br />
源代码连接:
http://www.shnenglu.com/Files/richardzeng/MVOTest.rar
ȝQ画L资源l承resource
Z避免发生资源泄露和resource的管?br />设计ResourceManagerc,负责资源的创建,加蝲和卸载以及删?/p>
两个抽象c?Resource ?ResourceManager
两个具体c?ConcreateResource ?ConcreateResourceManager
分别z于上面的抽象c?/p>
以上设计是看?OGRE 游戏引擎的资源管理部分,
对它的资源管理类ResourceManager不是很理?/p>
resource z了pen,brush{类
pencd以来自文Ӟ也可以自己创建SubPen d到SubPenList?/p>
ResourceManager 负责创徏资源Resource
1. 如果我在抽象?ResourceManager 声明 createRes函数Qƈq回基类resource
势必会要强制转换Q然后在用到具体的Resource时候又要{换回?/p>
2. 如果我在具体c?ConcreateResourceManager 声明 createConcreateRes函数
那么q费了我应用设计模式设计这么多c?/p>
// abstract class for resource
class Resource{
public:
// standard constructor
Resource(const string& name, const string& group)
:mName(name),mGroup(group){}
~Resource(){}
protected:
// prevent default construct
Resource():mName(""),mGroup(""){}
string mName;
string mGroup;
static unsigned long mHandle;
};
// subclass of resource
// concreateResource such as PEN
class Pen:
public Resource{
Pen(const string& name, const string& group)
:Resource(name,group){}
~Pen(){}
void loadfromFile(string& filename);
// add into vector
void addSubPen(SubPen* sub){
mSubPenList.push_back(sub);
}
public:
typedef std::vector<SubPen> SubPenList;
SubPenList mSubPenList;
};
class
// abstract class for resource manager
class ResourceManager{
public:
ResourceManager(){}
~ResourceManager(){}
public:
// here , I cannot understand OGRE degsin
Resource* createRes(const string& name,const string& group);
// resource map
typedef std::map<string,Resource*> ResourceMap;
ResourceMap mResources;
};
// subclass ResourceManager
class ConcreateResourceManager
:public ResourceManager
{
ConcreateResourceManager(){}
~ConcreateResourceManager(){}
// how can design here!!
Pen* createPen(const string& name,const string& group){}
}
public:
int lineStyle;
int lineWidth;
COLORREF lineColor;
public:
virtual void Serialize(CArchive& ar);
};
// SPen.cpp : 实现文g
//
#include "stdafx.h"
#include "ArchiveTest.h"
#include "SPen.h"
// SPen
IMPLEMENT_SERIAL(SPen,CObject,1)
SPen::SPen()
{
lineStyle = PS_SOLID;
lineWidth = 2;
lineColor = RGB(255,0,0);
}
SPen::~SPen()
{
}
// SPen 成员函数
void SPen::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{ // storing code
ar<<lineStyle;
ar<<lineWidth;
ar<<lineColor;
}
else
{ // loading code
ar>>lineStyle;
ar>>lineWidth;
ar>>lineColor;
}
}
///////////////////////////////////////
// 关键是要实现如何保存一pd的SPen对象
// load ?save函数实现打开ȝ文gQ文件的内容是一pdSPen对象Q?br />// for example
/** SPenCollection pc;
pc.Load("C:\\1.pen");
**/
#pragma once
// SPenCollection 命o目标
#include "SPen.h"
#include <afxtempl.h>
class SPenCollection : public CObject
{
DECLARE_SERIAL(SPenCollection)
public:
SPenCollection();
virtual ~SPenCollection();
void AddPen(SPen* pen);
void Load(CString strFileName);
void Save(CString strFileName);
// CArray 不知道用得对不对Q请大虾指教
CArray<SPen*,SPen*> pens;
};
#define M_PI 3.141
#include <math.h>
class Vector3D{
Vector3D(){x=y=z=0.0;}
Vector3D(double vx, double vy,double vz=0.0){
x = vx;
y = vy;
z = vz;
}
double magnitude() const{
return sqrt(x*x+y*y+z*z);
}
double dotP(const Vector3D& v1,const Vector3D& v2){
return (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z);
}
// get the vector angle
double angle() const{
double ret = 0.0;
double m = magnitude();
if (m>1.0e-6) {
// 问题出在q里Q!Q!
// ==============================
double dp = dotP(*this,Vector3D(1.0,0.0));
//==============================
if (dp/m>=1.0) {
ret = 0.0;
}
else if (dp/m<-1.0) {
ret = M_PI;
}
else {
ret = acos( dp / m);
}
if (y<0.0) {
ret = 2*M_PI - ret;
}
}
return ret;
}
protected:
double x;
double y;
double z;
};#pragma once
#define M_PI 3.141
#include <math.h>
class Vector3D{
Vector3D(){x=y=z=0.0;}
Vector3D(double vx, double vy,double vz=0.0){
x = vx;
y = vy;
z = vz;
}
double magnitude() const{
return sqrt(x*x+y*y+z*z);
}
double dotP(const Vector3D& v1,const Vector3D& v2){
return (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z);
}
// get the vector angle
double angle() const{
double ret = 0.0;
double m = magnitude();
if (m>1.0e-6) {
double dp = dotP(*this,Vector3D(1.0,0.0));
if (dp/m>=1.0) {
ret = 0.0;
}
else if (dp/m<-1.0) {
ret = M_PI;
}
else {
ret = acos( dp / m);
}
if (y<0.0) {
ret = 2*M_PI - ret;
}
}
return ret;
}
protected:
double x;
double y;
double z;
};
#include <iostream>
using namespace std;
//单g模板c?/div>
template<typename T> class Singleton
{
protected:
static T* m_Instance;
Singleton(){}
virtual~Singleton(){}
public:
//实例的获?/div>
static T* Instance()
{
if(m_Instance==0)
m_Instance=new T;
return m_Instance;
}
//单gcȝ释放
virtual void Release()
{
if(m_Instance!=0)
{
delete m_Instance;
m_Instance=0;
}
}
};
//单g模板试c?/div>
class Test:public Singleton<Test>
{
friend class Singleton<Test>; //声明为友员,不然会出?/div>
protected:
Test()
{
a=b=c=0;
}
virtual ~Test(){}
public :
int a;
int b;
int c;
};
//初始化静态成员。。?/div>
template<> Test*Singleton<Test>::m_Instance=0;
//以下为测试代?/div>
void main()
{
Test*t=Test::Instance();
t->a=5;
t->b=25;
t->c=35;
cout<<"t: a="<<t->a<<" b="<<t->b<<" c="<<t->c<<endl;
Test*t2;
t2=Test::Instance();
cout<<"t2 a="<<t2->a<<" b="<<t2->b<<" c="<<t2->c<<endl;
t2->Release();
}
|
但是有的时候不q回M东西是不行的ѝ?/p>
q回引用Q返回指针,q回对象到底怎么写?Q?br />
—————————————————————————————?br />下面是IQ中的内?/p>
据说爱因斯坦曾提q样的徏议:可能地让事情简单,但不要过于简单。在c++语言中相似的说法应该是:可能地使程序高效,但不要过于高效?/p>
一旦程序员抓住了“传值”在效率上的把柄Q参见条?2Q,他们会变得十分极端,恨不得挖出每一个隐藏在E序中的传值操作。岂不知Q在他们不懈地追求纯_的“传引用”的q程中,他们会不可避免地犯另一个严重的错误Q传递一个ƈ不存在的对象的引用。这׃是好事了?/p>
看一个表C有理数的类Q其中包含一个友元函敎ͼ用于两个有理数相乘:
class rational {
public:
rational(int numerator = 0, int denominator = 1);
...
private:
int n, d; // 分子和分?/p>
friend
const rational // 参见条款21Qؓ什?br /> operator*(const rational& lhs, // q回值是const
const rational& rhs)
};
inline const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
很明显,q个版本的operator*是通过传D回对象结果,如果不去考虑对象构造和析构时的开销Q你是在逃避作ؓ一个程序员的责仅R另外一件很明显的事实是Q除非确实有必要Q否则谁都不愿意承担q样一个时对象的开销。那么,问题归l于Q确实有必要吗?
{案是,如果能返回一个引用,当然没有必要。但误住,引用只是一个名字,一个其它某个已l存在的对象的名字。无Z时看C个引用的声明Q就要立即问自己Q它的另一个名字是什么呢Q因为它必然q有另外一个什么名字(见条ƾm1Q。拿operator*来说Q如果函数要q回一个引用,那它q回的必L其它某个已经存在的rational对象的引用,q个对象包含了两个对象相乘的l果?/p>
但,期望在调用operator*之前有这样一个对象存在是没道理的。也是_如果有下面的代码Q?/p>
rational a(1, 2); // a = 1/2
rational b(3, 5); // b = 3/5
rational c = a * b; // c ?3/10
期望已经存在一个gؓ3/10的有理数是不现实的。如果operator* 一定要q回q样一个数的引用,必自己创个数的对象?/p>
一个函数只能有两种Ҏ(gu)创徏一个新对象Q在堆栈里或在堆上。在堆栈里创建对象时伴随着一个局部变量的定义Q采用这U方法,pq样写operator*Q?/p>
// 写此函数的第一个错误方?br />inline const rational& operator*(const rational& lhs,
const rational& rhs)
{
rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
q个Ҏ(gu)应该被否冻I因ؓ我们的目标是避免构造函数被调用Q但result必须要象其它对象一栯构造。另外,q个函数q有另外一个更严重的问题,它返回的是一个局部对象的引用Q关于这个错误,条款31q行了深入的讨论?/p>
那么Q在堆上创徏一个对象然后返回它的引用呢Q基于堆的对象是通过使用new产生的,所以应该这样写operator*Q?/p>
// 写此函数的第二个错误Ҏ(gu)
inline const rational& operator*(const rational& lhs,
const rational& rhs)
{
rational *result =
new rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
首先Q你q是得负担构造函数调用的开销Q因为new分配的内存是通过调用一个适当的构造函数来初始化的Q见条款5和m8Q。另外,q有一个问题:谁将负责用delete来删除掉new生成的对象呢Q?/p>
实际上,q绝Ҏ(gu)一个内存泄漏。即使可以说服operator*的调用者去取函数返回值地址Q然后用deleted除它Q绝对不可能——条?1展示了这L代码会是什么样的)Q但一些复杂的表达式会产生没有名字的时|E序员是不可能得到的。例如:
rational w, x, y, z;
w = x * y * z;
两个对operator*的调用都产生了没有名字的临时|E序员无法看刎ͼ因而无法删除。(再次参见条款31Q?/p>
也许Q你会想你比一般的熊——或一般的E序员——要聪明Q也许,你注意到在堆栈和堆上创徏对象的方法避免不了对构造函数的调用Q也许,你想起了我们最初的目标是ؓ了避免这U对构造函数的调用Q也许,你有个办法可以只用一个构造函数来搞掂一切;也许Q你的眼前出Cq样一D代码:operator*q回一个“在函数内部定义的静态rational对象”的引用Q?/p>
// 写此函数的第三个错误Ҏ(gu)
inline const rational& operator*(const rational& lhs,
const rational& rhs)
{
static rational result; // 要作ؓ引用q回?br /> // 静态对?/p>
lhs和rhs 怹Q结果放qresultQ?/p>
return result;
}
q个Ҏ(gu)看v来好象有戏,虽然在实际实C面的伪代码时你会发现Q不调用一个rational构造函数是不可能给出result的正值的Q而避免这L调用正是我们要谈论的主题。就你实现了上面的伪代码,但,你再聪明也不能最l挽救这个不q的设计?/p>
想知道ؓ什么,看看下面q段写得很合理的用户代码Q?/p>
bool operator==(const rational& lhs, // rationals的operator==
const rational& rhs); //
rational a, b, c, d;
...
if ((a * b) == (c * d)) {
处理相等的情?
} else {
处理不相{的情况;
}
看出来了吗?((a*b) == (c*d)) 会永qؓtrueQ不aQbQc和d是什么|
用等L函数形式重写上面的相{判断语句就很容易明白发生这一可恶行ؓ的原因了Q?/p>
if (operator==(operator*(a, b), operator*(c, d)))
注意当operator==被调用时QL两个operator*刚被调用Q每个调用返回operator*内部的静态rational对象的引用。于是,上面的语句实际上是请求operator==对“operator*内部的静态rational对象的值”和“operator*内部的静态rational对象的值”进行比较,q样的比较不相等才怪呢Q?/p>
q运的话Q我以上的说明应该以说服你Q想“在象operator*q样的函数里q回一个引用”实际上是在费旉。但我没q稚C怿q运M光自己。一些h——你们知道这些h是指谁——此M在想Q“唔Q上面那个方法,如果一个静态变量不够用Q也许可以用一个静态数l……?/p>
请就此打住!我们Nq没受够吗?
我不能让自己写一D늤例代码来太高q个设计Q因为即使只抱有上面q种x都以o人感到羞愧。首先,你必选择一个nQ指定数l的大小。如果n太小Q就会没地方储存函数q回|q和我们前面否定的那个“采用单个静态变量的设计”相比没有什么改q。如果n太大Q就会降低程序的性能Q因为函数第一ơ被调用时数l中每个对象都要被创建。这会带来n个构造函数和n个析构函数的开销Q即使这个函数只被调用一ơ。如果说"optimization"Q最优化Q是指提高Y件的性能的过E, 那么现在q种做法直可以称?pessimization"Q最差化Q。最后,x怎么把需要的值放到数l的对象中以及需要多大的开销Q在对象间传值的最直接的方法是通过赋|但赋值的开销又有多大呢?一般来_它相当于调用一个析构函敎ͼ摧毁旧|再加上调用一个构造函敎ͼ拯新|。但我们现在的目标正是ؓ了避免构造和析构的开销啊!面对现实吧:q个Ҏ(gu)也绝对不能选用?/p>
所以,写一个必返回一个新对象的函数的正确Ҏ(gu)是让这个函数返回一个新对象。对于rational的operator*来说Q这意味着要不是下面的代码(是最初看到的那段代码Q,要不是本质上和它等L代码Q?/p>
inline const rational operator*(const rational& lhs,
const rational& rhs)
{
return rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
的确Q这会导致“operator*的返回值构造和析构时带来的开销”,但归根结底它只是用小的代h来正的E序q行行ؓ而已。况且,你所担心的开销q有可能永远不会出现Q和所有程序设计语a一Pc++允许~译器的设计者采用一些优化措施来提高所生成的代码的性能Q所以,在有些场合,operator*的返回g被安全地除去Q见条款m20Q。当~译器采用了q种优化Ӟ当前大部分编译器q么做)Q程序和以前一Ll工作,只不q是q行速度比你预计的要快而已?/p>
以上讨论可以归结为:当需要在q回引用和返回对象间做决定时Q你的职责是选择可以完成正确功能的那个。至于怎么让这个选择所产生的代价尽可能的小Q那是编译器的生产商L的事?/p>
#include <iostream>
using namespace std;
void Insert(char *s, char *s1, int i)
{
char *p,*q;
p = s + strlen(s); // p 指向s的末?1
q = p + strlen(s1); //q 指向新构造的字符串的\0
*q = '\0';
//
for(p--,q--;p>=s+i-1;)
{
*(p--) = *(q--);
}
//
for(p=s+i-1;*s1;)
{
*(p++) = *(s1++);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char *s = "Student";
char *s1 = "Teacher";
Insert(s,s1,3);
// 期待的输出是StuTeacherdent;
cout<<s;
return 0;
}
// q有我如果把insert函数Ҏ(gu)下面的应该也是可以的?br />
void Insert2(char *s, char *s1, int i)
{
char *p,*q;
p = s + strlen(s); // p 指向s的末?1
q = p + strlen(s1); //q 指向新构造的字符串的\0
*q = '\0';
//
for(p--,q--;p>=s+i-1;)
{
*p-- = *q--;
}
//
for(p=s+i-1;*s1;)
{
*p++ = *s1++;
}
}