内存对齐指令
通常来说内存对齐很能提高速度的,使用如下指令在两个操作系统下面令内存对齐
#ifdef _WIN32
typedef __declspec(align(16)) struct
#elif _LINUX
typedef attribute((aligned(16))) struct
#endif
这里有一篇讲得很好的文章
通常来说内存对齐很能提高速度的,使用如下指令在两个操作系统下面令内存对齐
#ifdef _WIN32
typedef __declspec(align(16)) struct
#elif _LINUX
typedef attribute((aligned(16))) struct
#endif
这里有一篇讲得很好的文章
经常在blog上写些东西,可是有些心底的东西还是无法表达,因为面对的都是认识的人,害怕有些东西被看穿,毕竟,人还是要有点秘密的
近来心情非常的不好,茫然、迷茫、,对于生活缺乏所有的信心,不管做什么事,我都觉得自己是一个局外人,什么都不会,我经常会怀疑自己的能力,这么多年,是怎么考上大学的,在大学里都学了些什么,为什么接触工作之后,我什么都不会,为什么面对同样的问题,我总是最后一个才理解,有些到最后也不理解,我经常脑袋里一片空空,不知道干什么,静下心来看书的时间一般不会超过十分钟,当碰到问题的时候又不知道该如何去解决。我真的快要崩溃了。
呵呵,学习之余,做歌一首:
加加难
作者: 小小C
C++之难,难于上青天!
自学路上多险阻,只身孤闯山林间.
又无大师指明路,摔跤绊倒是平闲.
整型浮点分不清,数据转换失心眼.
最怕指针空中悬,莫名其妙内存泄.
又恐数组越了位,运行期间报危险.
指针引用似兄弟,使用不当却翻脸.
const功能虽广泛,理解还需费时间.
自增自减应注意,前缀后缀意思变.
运算符号分不清,胡乱重载图方便.
判断循环结构难,拍桌撞墙快疯癫.
类与对象好难懂,一个概念想半天.
请位高手来讲解,越说越乱直转圈.
到了最后说不懂,鼻孔顿出斤鼻血.
无可奈何自己上,锥股悬头到半夜.
咣当一声五窍通,拔开乌云见青天.
急忙登堂又入室,不料难题阻眼前.
继承还分父与子,多态又出虚方法.
公有私有才分清,隐性显性头晕眩.
构造析构又重载,抽象封装还友元.
异常处理要细心,模板容器需常练.
冥思苦想类架构,脑袋抓破头冒烟.
狠下决心写游戏,万行代码出指间.
提心吊胆去编译,千条错误直瞪眼.
心理承受能力低,差点进了疯人院!
大哭一场砸机器,硬着头皮又重写.
积累经验熟生巧,程序出炉功效见.
被人高手一声叫,面红心跳喜笑颜.
拿来新手问题看,抓耳挠腮没法解.
浪得高手名儿虚,空得欢喜只一叹:
——C++之难,难于上青天!
Game Developer Magazine 1994 - 2000年,共7年的游戏开发者杂志电子版(含源码)
Graphics Programming Black Book (by Michael Abrash),图形编程黑书,Id software的Michael Abrash编著
如何为我的游戏实现一个UI系统,这个问题我想了很久,不过我现在可不像开始的时候那样一点思路也没有。如果你也被这个问题所困扰,我十分乐意与你分享这几天来的学习成果。嘿嘿,我是不是有点得意忘形了?
7.1 什么是用户界面库(UI LIB)?
程序员总是喜欢捷径,没有人希望做重新发明车轮的事。在开发程序的时候,我们总是想法设法的包含各式各样的库,通过那些事先写好的函数来完成我们的工作。例如,文件读写函数或者printf和scanf例程允许我们完成不同的任务而不需要学习硬件的细节。因此,使用库可以节省我们的开发时间并且使我们的软件兼容性更好。我们在第二部分开发的UI LIB同样会为界面开发人员提供这样的好处。最终,它将包含一组类和函数来帮助我们在短时间内开发出一流的界面。对于那些使用我们库的开发者来说,他们只需要简单的在工程中添加#include <UILIB.h>和适当的lib文件就可以获得全部的功能。下面让我仔细看看UI LIB由什么组成。
7.2 像类一样的控件
在第一章中我们曾解释过如何用一组控件制作界面,像按钮、列表框、文本框和复选按钮等,以及用户和程序是如何通过控件通信的。因此,UI LIB也将会是一组控件的集合。
7.3 控件——类的层次和基础控件
开发UI LIB从哪里开始最好呢?我们应该从开发一个按钮、文本框或者一个下拉列表开始吗?或者有什么基本的开发结构是我们必须遵守的?实际上,最好的开始是问问自己什么是控件。只有这么做,我们才能知道什么是所有控件的共同属性。实际上,只要它包含这些属性它就可以被认为是一个控件了。随着章节的进程我们将逐个检测这些属性。根据类的特点,阶段性的开发这些控件意义重大,我们将从一个基础类或者基础控件开始。它仅包含了所有控件的基本属性,什么也不多。其他的控件,诸如按钮和标签,都将从它派生出来。这样我们就不必为每一个控件单独编写相同的功能了。我们把这个基础类命名为CXControl,我们将用两章讲解它。图7.1展示了UI LIB的层次结构。
注意
我用了两章讲解CXControl,因为它是一个那样庞大并且重要的概念。我为这个类添加了大量的功能,以便定制那些派生类的工作简单并且迅速。
7.4 CXControl——旅行的开始
在UI LIB中CXControl作为一个基类出现,其他的类皆从CXControl派生而来。作为其他类的祖先,CXControl为它们提供了一组共有的特征。本章致力于CXControl的开发,后面的部分研究了什么才是所有控件的共有属性以及如何在CXControl中实现它们。开发将从一个空白的类的声明开始,随着研究的深入,我们将会慢慢地为它填充内容。
7.5 定义CXControl——控件和画布
图7.2 一张空白的画布
不同的控件之间有着明显的区别,列表框是一个外观,按钮则是另外一个样子的。但是所有的控件都表现为其父控件边界内的一块矩形区域,控件在这个区域内绘制自己。在术语中,这块绘制图像的矩形区域被称为画布(canvas)。实际上,它就是像表面(surface)或纹理(texture)那样的一组像素。它的大小用宽(width)和高(height)表示,显示状态分为可见和不可见。下面的代码展示了canvas是如何实现的。
class CXControl
...{
protected:
DWORD m_Width; //画布的宽
DWORD m_Height; //画布的高
bool m_Visible; //画布是否可视
CXTexture * m_Canvas; //指向画布的指针
CXPen * m_Pen; //一些要画在画布上的东西
public:
CXTexture * GetCanvas(void) ...{return m_Canvas;}
void SetCanvas(CXTexture * Texture) ...{m_Canvas = Texture;}
bool GetVisible(void) ...{return m_Visible;}
void SetVisible(bool Visible) ...{m_Visible = Visible;}
CXPen * GetPen(void) ...{return m_Pen;}
void SetPen(CXPen * Pen) ...{m_Pen = Pen;}
DWORD GetWidth(void) ...{return m_Width;}
DWORD GetHeight(void) ...{return m_Height;}
void SetWidth(DWORD Width) ...{m_Width = Width;}
void SetHeight(DWORD Height) ...{m_Height = Height;}
};

注:绘制的细节将在下一章研究消息和事件响应的时候介绍。另外,像media player使用的那种非矩形控件不在本书的讨论范围内。
7.6 父控件、兄弟控件、子控件
第一章曾简要地提到过界面中各种控件的层次关系。因此,控件之间是密切相关的。例如,除了桌面之外这些界面中顶层的控件是没有父控件的。通常,这类控件用作应用程序的主窗口,它包含着按钮、复选按钮之类的其他控件。这些控件是一个窗口的孩子,是彼此的兄弟,而这个窗口就是它们的父亲。实际上,这种层次关系对控件来说是最重要的影响之一,在应用程序创建和销毁它们的时候就决定了。看图7.3来想象一下这种层次关系。
图7.3
在先前的章节中,我们研究过如何使用链表来有效地管理鼠标指针列表。现在我们将使用一个改进了的方法来处理控件之间的关系。还记得吗,链表就是一个线性的项的列表,其中每一项都有一个指针指向它的下一项,但最后一项是个例外,它的指向为空(NULL)。这是存储像控件的孩子那样的项的理想方式,但缺点是你只能在链表上沿着一个方向移动。虽然不是什么大问题,但这是多么的不方便和不切实际啊。解决这个问题的办法就是使用双向链表。这样,每个控件都维持了指向前后兄弟的指针,换句话说就是用两个指针分别指向链表中此控件的前一控件和后一控件。对于开发者来说,这样的安排有几个好处:一、你可以在此列表上双向移动,从任意一点开始到任意一点结束;二、你可以删除任意的项,然后将缺口修补好;三、你可以完成所有形式的排序以及重新整理项的操作。看图7.4理解双向链表的概念。
图7.4
因此,使用双向链表来实现控件之间的关系是不错的选择。要为CXControl添加这样一个列表来操纵它的孩子只需要简单地添加几个不同的指针:一个指向父控件,一个指向前后兄弟控件(译者:其实就是两个),一个指向第一个子控件。为了管理这些指针,我们还得添加几个函数,这包括添加子控件的函数、通过兄弟列表操纵的函数和删除子控件的函数。这些在后面的小节中都有讲解。先看一下修改过的CXControl类的声明。
class CXControl
...{
protected:
DWORD m_Width;
DWORD m_Height;
bool m_Visible;
CXTexture * m_Canvas;
CXPen * m_Pen;
CXControl * m_ChildControls;
CXControl * m_NextSibling;
CXControl * m_PreviousSibling;
CXControl * m_Parent;
public:
// Accessors
CXTexture * GetCanvas(void) ...{return m_Canvas;}
void SetCanvas(CXTexture * Texture) ...{m_Canvas = Texture;}
bool GetVisible(void) ...{return m_Visible;}
void SetVisible(bool Visible) ...{m_Visible = Visible;}
CXPen * GetPen(void) ...{return m_Pen;}
void SetPen(CXPen * Pen) ...{m_Pen = Pen;}
DWORD GetWidth(void) ...{return m_Width;}
DWORD GetHeight(void) ...{return m_Height;}
void SetWidth(DWORD Width) ...{m_Width = Width;}
void SetHeight(DWORD Height) ...{m_Height = Height;}
CXControl * GetParentControl(void) ...{return m_Parent;}
void SetParentControl(CXControl * Control) ...{m_Parent = Control;}
CXControl * GetNextSibling(void) ...{return m_NextSibling;}
void SetNextSibling(CXControl * Control) ...{m_NextSibling = Control;}
CXControl * GetPreviousSibling(void) ...{return m_PreviousSibling;}
void SetPreviousSibling(CXControl * Control) ...{m_PreviousSibling = Control;}
CXControl * GetFirstChild(void) ...{return m_ChildControls;}
void SetFirstChild(CXControl * Control) ...{m_ChildControls = Control;}
CXControl * AddChildControl(CXControl * Control);
CXControl * RemoveChildControl(CXControl * Control);
void RemoveAllChildren();
int GetChildCount();
};

7.6.1 添加子控件
控件通过m_ChildControls指针存储其子控件的信息。如果要把一个已存在的控件变成另外一个控件的孩子,你需要调用CXControl的AddChildControl方法。看看这个函数的定义,是不是有点眼熟?
注意,这个函数与前一章把光标添加到链表的函数稍有不同。这里我们创建的是一个双向链表,因此,除了后一个兄弟控件之外,前一个兄弟控件也需要设置。这样我们才能双向的操纵这个列表。
CXControl * CSControl::AddChildControl(CXControl * Control)
...{
Control->SetParentControl(this);
CXPen * Pen = Control->GetPen();
SAFE_DELETE(Pen);
Control->SetPen(this->GetPen());
if(!m_ChildControls)
m_ChildControls = Control;
else
...{
CXControl * Temp = this->GetFirstChild();
while(Temp->GetNextSibling())
Temp = Temp->GetNextSibling();
Temp->SetNextSibling(Control);
Control->SetPreviousSibling(Temp);
}
return Control;
}

7.6.2 清除子控件
清除子控件就是将它们全部删除的过程。要达到这个目的,调用CXControl的RemoveAllChildren方法就可以了。在前一章,我们展示过一个类似的过程,请看下面的函数定义。
void CXControl::RemoveAllChildren()
...{
CXControl * Temp = this->GetFirstChild();
while(Temp)
...{
CXControl * Next = Temp->GetNextSibling();
SAFE_DELETE(Temp);
Temp = Next;
}
}

7.6.3 删除指定的子控件
图7.5
在前一章我们没有见到过如何删除列表中任意位置的项,而双向链表使这个过程变得简单了。例如,我们想要删除项目I,只要完成一下步骤:用N指向I的后一个兄弟控件,P指向I的前一个兄弟控件,然后删除I,最后将P的后一个兄弟指向N。看图和下面的定义你可以很快理解它。
CXControl * CXControl::RemoveChildControl(CXControl * Control)
...{
CXControl * Next = Control->GetNextSibling();
CXControl * Previous = Control->GetPreviousSibling();
SAFE_DELETE(Control);
Next->SetPreviousSibling(Previous);
Control = Next;
return Control;
}

7.6.4 统计子控件的数量
有时候如果能知道指定的控件有多少子控件会很有帮助。计算它们很简单。只要遍历它的子控件并增加计数器就可以了。你可以调用CXControl的GetChildCount来完成此功能,函数定义如下:
int CXControl::GetChildCount()
...{
int Count = 0;
CXControl * Temp = this->GetFirstChild();
while(Temp)
...{
CXControl * Next = Temp->GetNextSibling();
Count++;
Temp = Next;
}
return Count;
}

7.7 绝对坐标和相对坐标
图7.6
注意,这个按钮的绝对坐标和相对坐标是不一样的。一个表示的是它在屏幕上的位置,而另一个表示的是它在它的父控件中的位置。
第7.5小节解释过什么是画布以及任何可视的东西本质上都是控件。它示范了如何用宽和高描述一个控件的大小,如何用可见和不可见表示控件的显示状态。但是我们忽略了另外一个属性——坐标。很显然,每一个控件都有X和Y两个坐标。坐标又分绝对坐标和相对坐标两种。绝对坐标是人们想起坐标时立即跳进人们脑子的想法,它是从屏幕的左上角开始计算的。相对坐标是相对于它的父控件来说的,换句话说,它是从其父控件的左上角开始计算的。有些人可能想问这个区别是否真的重要。我可以十分肯定的回答你,是的。为什么?看看图7.6你就明白了。在UI LIB中,所有的控件都使用的是相对坐标,因为它比绝对坐标更简单更直观。虽然有时候我们也不得不计算它的绝对坐标,你会在下一小节看到如何实现它。
class CXControl 
...{
public:
CXControl();
virtual ~CXControl();
protected:
D3DXVECTOR2 m_Position;
DWORD m_Width;
DWORD m_Height;
bool m_Visible;
CXTexture * m_Canvas;
CXPen * m_Pen;
CXControl * m_ChildControls;
CXControl * m_NextSibling;
CXControl * m_PreviousSibling;
CXControl * m_Parent;
public:
// Accessors
CXTexture * GetCanvas(void) ...{return m_Canvas;}
void SetCanvas(CXTexture * Texture) ...{m_Canvas = Texture;}
bool GetVisible(void) ...{return m_Visible;}
void SetVisible(bool Visible) ...{m_Visible = Visible;}
CXPen * GetPen(void) ...{return m_Pen;}
void SetPen(CXPen * Pen) ...{m_Pen = Pen;}
DWORD GetWidth(void) ...{return m_Width;}
DWORD GetHeight(void) ...{return m_Height;}
void SetWidth(DWORD Width) ...{m_Width = Width;}
void SetHeight(DWORD Height) ...{m_Height = Height;}
CXControl * GetParentControl(void) ...{return m_Parent;}
void SetParentControl(CXControl * Control) ...{m_Parent = Control;}
CXControl * GetNextSibling(void) ...{return m_NextSibling;}
void SetNextSibling(CXControl * Control) ...{m_NextSibling = Control;}
CXControl * GetPreviousSibling(void) ...{return m_PreviousSibling;}
void SetPreviousSibling(CXControl * Control) ...{m_PreviousSibling = Control;}
CXControl * GetFirstChild(void) ...{return m_ChildControls;}
void SetFirstChild(CXControl * Control) ...{m_ChildControls = Control;}
D3DXVECTOR2 * GetPosition(void) ...{return &m_Position;}
FLOAT GetXPos(void) ...{return m_Position.x;}
FLOAT GetYPos(void) ...{return m_Position.y;}
void SetXPos(FLOAT X) ...{m_Position.x = X;}
void SetYPos(FLOAT Y) ...{m_Position.y = Y;}
void SetXYPos(FLOAT X, FLOAT Y);
CXControl * AddChildControl(CXControl * Control);
CXControl * RemoveChildControl(CXControl * Control);
void RemoveAllChildren();
int GetChildCount();
void GetAbsolutePosition(D3DXVECTOR2 * Position);
};
注意
在层次结构中,像应用程序主窗口这样的顶层控件的绝对坐标和相对坐标是一样的。这是因为除了桌面以外,顶层控件没有父控件,而桌面覆盖了整个屏幕。(译者:我一直都把桌面作为顶层控件的父控件,而桌面的ParentControl为NULL)
7.7.1 计算坐标
图7.7
通过一个控件的相对坐标可以轻松地计算出它的绝对坐标。这使得拖动窗口在屏幕上移动时控件正确的重绘变得简单,因为控件与控件之间的相对坐标是不变的。
CXControl的GetAbsolutePosition方法可以返回一个控件的绝对坐标,也就是控件在整个屏幕上的坐标。为了正确的绘制控件,我们会经常用到绝对坐标。计算控件的绝对坐标是一个简单的过程:你可以简单地用控件的相对坐标加上其父控件的绝对坐标。实际上,这是从一个控件到它的顶层父控件的相对坐标的累积。函数定义如下。
void CXControl::GetAbsolutePosition(D3DXVECTOR2 * Position)
...{
Position->x+=this->GetXPos();
Position->y+=this->GetYPos();
if(this->m_Parent)
this->m_Parent->GetAbsolutePosition(Position);
}

7.8 类CXControl目前的声明
代码同7.7,略
7.9 总结
本章以CXControl的形式初步介绍了UI LIB。这个类包含了所有控件都必须拥有的一般属性。下一章我们将继续深入这个主题。但在继续之前,我们复习一下所学内容。
■库是能完成某一任务的函数、结构和类的集合。库主要是通过提供完成任务的工具来减轻程序开发者的负担。像DirectX就是一个例子。
■UI LIB是User Interface Library的缩写。它由像按钮、列表框、复选框之类的一组控件组成。开发者可以用它为自己的软件创建用户界面。
■即使控件千差万别,但它们都继承了一个共有的属性集。这就是为什么开发CXControl。虽然它本身不能独立实例化,但它作为一个基类出现,为其他控件提供基础特征集。
■在几何学上,控件是一个典型的被称为画布的矩形区域。它可以用宽和高,可见和不可见表示。控件在画布区域内绘制自己。因此,按钮有一个外观,而列表是另一个。
■界面中的每一个控件都存在在一个层次结构中。顶层的控件被认为是最终的祖先或根控件,通常用作程序的主窗口。其他的控件则是它的子孙,它们同样也可以有兄弟和孩子。
■控件有两个坐标,一个绝对坐标,一个相对坐标。前者表示的是控件从屏幕左上角开始计算的真实坐标,后者表示的是从其父控件左上角开始计算的坐标。无论何时在屏幕上移动一个窗口,这个程序对重新调整控件都很有用处。
声明:本书的英文版权归原作者所有,我翻译的这些版权自然归我。你可以下载到本地保存留念,但在未取得本人书面许可时,谢绝任何形式的转载。你如果将其用于商业目的,请先与原英文版版权所有者和我联系,以免引起不必要的麻烦。
一个程序只运行一个实例(或限制实例数量)通常可以采用如下方法: 1)FindWindow 之<窗口标题> 通过查找窗口标题来确定上一实例是否正在运行,不适合窗口标题动态变化的程序。 2)FindWindow 之<任务栏按纽标题> 通过查找任务栏按纽标题来确定上一实例是否正在运行,不适合按纽标题动态变化的程序(如Winamp)。通常情况下,该方法还是优先考虑,因为按纽标题是一般是固定的。 3)Window Property 将某个数据(可以是字符串或句柄)通过SetProp加入到指定窗口的property list,程序运行时枚举窗口并检查该数据是否存在来确定上一实例是否正在运行。 4)全局Atom 将某个特定字符串通过GlobalAddAtom加入全局原子表(Global Atom Table),程序运行时检查该串是否存在来确定上一实例是否正在运行。该方法有个局限,就是程序终止前必须显式调用GlobalDeleteAtom来释放atom,否则该atom不会自动释放,如果程序运行时意外终结了,那么下一个实例就无法正常执行。早期版本的realplayer就存在这个现象,不知道是不是采用了该方法。 5)Mutex/Event/Semaphore 通过互斥对象/信号量/事件等线程同步对象来确定实例是否存在,在NT下要注意权限问题(SID)。 6)DLL全局共享区域 VC下的DLL工程可以通过下面代码来建立一个进程间共享数据段: #pragma data_seg(".share") //shared for all processes that attach to the dll DWORD dllgs_dwRunCount = 1; //一定要在这里对变量进行初始化,否则工夫白做! #pragma data_seg() #pragma comment(linker,"/section:.share,rws") 导出3个函数,分别为: DWORD IncRunCount(void); //运行计数器加1,返回计数器结果 DWORD DecRunCount(void); //运行计数器减1,返回计数器结果 DWORD GetRunCount(void); //取当前运行计数器 由于DLL全局共享段在映射到各个进程地址空间时仅会被初始化一次,并且是在首次被windows加载时,所以利用该共享段数据就能对程序实例进行可靠计数。 7)内存映射文件(File Mapping) 通过把程序实例信息(如窗口句柄、计数器等等)放置到跨进程的内存映射文件,同样可以控制程序实例运行的数量,道理与DLL全局共享区域类似。 由于内存映射文件在映射到各个进程地址空间时会被初始化,所以利用该共享段数据就能对程序实例进行可靠计数。
文本文件读写的编码问题
当你的程序读一个文本文件时,如何判断文件中的字符是MBCS格式还是Unicode格式? Windows定义了一个"字节顺序标记"(Byte-order Mark)的概念:当一个txt文件的前2个字节为FF FE时,这个文件里面的字符采用Unicode编码, 如果没有字节顺序标记,就是MBCS编码。更多关于字节顺序标记的说明,请看 MSDN的官方资料。
团队,究竟是什么制度的?拓展训练据说是要问这个问题的。
在训练的一个游戏上,似乎感悟到了一点东西,不敢藏掖,遂贴于此。
10个人站在两根平行放置的木板上,两根木板很重,一个人抬不起来,每个木板上隔一段距离绑一根绳子,共绑有10根绳子。十个人站在这两根木板上,左右手各自抓握对应的一组绳子,然后向前迈进。
这个游戏的关键是:
1 当十个人同时迈左脚时,必须把重心全部放到右脚上而使左脚悬空,否则通过拉绳子是拉不起来左边的这块木板的。右边的木板也同理。
2 必须有人指挥,否则,即便9个人迈了左脚,只要有一个人迈了右脚,就走不动。
3 只有十个人同时迈左脚,或者迈右脚时,才能走动。
关键就是指挥。
前三个团队都是一个人指挥十个人,我们称这种制度为“民主集中制”。他们都在预定时间完成了任务,最快的一组:1分47秒。
我们团队的制度不同,我们是十个人同时呐喊口号,指挥者反倒晾在一边,这种制度大凡类似于“自由制”。结果是,我们团队速度最快,本可以在1分30秒左右冲线。由于十个人同时呐喊,在呐喊的同时每个人就知道自己该做什么,所以,速度非常快。十个人身形绝对统一,完美,近乎完美。
但,离终点只剩一点的地方,已经1分29秒了,我们遇到了一个小Bug。剩下的事情就……
十个人试图四次同时再次呐喊,但是每次,大家虽然都喊得很统一,但就是有人的身形无法跟上。第五次,不得不由一直被晾在一边的队长重新发令,我们破线的时候已经是2分了。
很可惜。
这似乎也折射出了团队制度的一些问题。
民主集中制,制度是一切,民主商议得到的结果,经由少数人形成决议,并将这些决议形成制度。而所有人,必须按照制度进行工作,不允许任何人有特殊化。民主集中制的结果,取决于决议形成的制度,也就是一开始的参议和决策过程。这个过程只要没有问题,那么最后最起码是能完成一个结果的,只不过可能不尽善尽美,但起码是可以达到的。这是大多数企业使用的管理模式——只不过很多企业是畸形的,参议权没有团队成员的份,少数人参议,少数人决议,少数人独裁——转化成了彻头彻尾的独裁制,在这个行业里混的兄弟姐妹们可不要告诉我你没看到过这种公司。好来不及鄙视他们,因为独裁不是我们的重点,而且这些企业现在都是某些势力的宠儿,别的不说什么,我们还得顾及自己的小命不是~。
民主集中制主要的效率问题是发生在执行期,执行的效率一般都比较低。因为制度和进度表一旦形成,人固有的惰性总会不由自主地向这个进度去靠,即便人们可以自觉自发地完成得更快更好,但是,制度相当于为人们的懒惰提供了法律依据。因此会有人说“项目总会超期,即便认识到这一点也是如此……”。但你如果是单枪匹马做一个小游戏,那么你反倒发现“项目总会早于预期的时间,即便认识到这一点也是如此……”了,这大约就是民主集中制的一些影响吧。^_^
另外,在民主集中制下,团队的凝聚力仅限于参议阶段,而非执行阶段,因此,也大打折扣。但与此同时,大团队的组织才有了可能。10个人的团队可以在执行期互通有无,拚命向前赶进度,但100个人呢?1000个人呢?
而自由制,它的好处是,对于小但素质高的团队、小目标而言,执行期的效率很高,每个人都会知道自己该做什么,也会努力去完成。但是,一旦发生方向性的或者重大的分歧,重新整合的难度会加大。
个人认为,民主集中制更适合正规而项目相对中型的团队,而自由制则适合于小型项目、小而稳定的团队,也就是小作坊。
制度没有好坏,没有最好的制度,没有最坏的制度,只有最适应的制度。一定的情况下,独裁也是好的抉择。用在团队上合适,用在社会上也合适。
制度就如同工具一般,只不过这种工具切换的时候总要有些麻烦,因此有时要刻意避免制度的切换。形而上学地去讨论制度的好坏,倒不如务实地分析自己所处的情况,选择对自己最有利的制度——哪怕是“德国国家社会主义”(考虑到某些神经脆弱的网友的请求,我以后绝对不会再用“纳粹”这个词)制度,在必要的历史时期也是可以选择的。
任何制度都会有弊端,也都会有相应地优点,因此,更重要的是分析自己,而不是盲目地跟随着上个世纪7、80年代的狂徒们一起去叫嚣“XX制度就是好”“XX制度就是不好”。已经21世纪了,我们毕竟应该像个人一样地活着,要有自己的头脑,用自己的分析,获得自己的结果。
嗯,好吧,偶然想到的也就写下来了。其实对于我大凡是没有区别的,作为团队的一员,发挥自己的参议权和主观能动性,在决议之后,按照已经生成的决议完成自己该做的工作,这大凡就是我们应该付出的责任吧。虽然我可能打心眼里喜欢那种狂放不羁的自由(谁不喜欢啊!),但是,或许更需要明白的是身为一个员工,身上沉甸甸的职责!
这是我收藏的一篇文章,每每在遇到挫折的时候就拿出来读一读,总是能给我动力!
这些日子我一直在写一个实时操作系统内核,已有小成了,等写完我会全部公开,希望能够为国内IT的发展尽自己一份微薄的力量。最近看到很多学生朋友和我当年一样没有方向 ,所以把我的经历写出来与大家共勉,希望能给刚如行的朋友们一点点帮助。 一转眼我在IT行业学习工作已经七年多了,这期间我做过网页,写过MIS、数据库,应用程序,做过通信软件、硬件驱动、协议栈,到现在做操作系统内核和IC相关开发,这中间走了很多弯路,也吃了不少苦。