#
摘要: 温故而知新,总结了多种单例模式的写法,考虑上多线程没有非常完美的,各取所需吧
//1.//Easy ,<<Pattern Design>>//Q://When to free the m_singleton space?//when to execute the ... 阅读全文
总用到这个转化,记下来
wchar_t* atow(char* src) { int dest_len; dest_len = MultiByteToWideChar(CP_ACP,0,src,-1,NULL,0); wchar_t *dest = new wchar_t[dest_len]; if(dest == NULL) return NULL; MultiByteToWideChar(CP_ACP,0,src,-1,dest,dest_len); return dest; }
static关键字在C和C++中的用法稍有区别,主要是C++扩展了static关键字的作用
当定义一个命名空间时,可以忽略这个命名空间的名称: namespce { char c; int i; double d; } 编译器在内部会为这个命名空间生成一个唯一的名字,而且还会为这个匿名的命名空间生成一条using指令。所以上面的代码在效果上等同于: namespace __UNIQUE_NAME_ { char c; int i; double d; } using namespace __UNIQUE_NAME_; 在匿名命名空间中声明的名称也将被编译器转换,与编译器为这个匿名命名空间生成的唯一内部名称(即这里的__UNIQUE_NAME_)绑定在一起。还有一点很重要,就是这些名称具有internal链接属性,这和声明为static的全局名称的链接属性是相同的,即名称的作用域被限制在当前文件中,无法通过在另外的文件中使用extern声明来进行链接。如果不提倡使用全局static声明一个名称拥有internal链接属性,则匿名命名空间可以作为一种更好的达到相同效果的方法。 注意:命名空间都是具有external 连接属性的,只是匿名的命名空间产生的__UNIQUE_NAME__在别的文件中无法得到,这个唯一的名字是不可见的. C++ 新的标准中提倡使用匿名命名空间,而不推荐使用static,因为static用在不同的地方,涵义不同,容易造成混淆.另外,static不能修饰class
原文地址:http://www.cnblogs.com/ly4cn/archive/2005/11/28/286185.html
指针,在C/C++语言中一直是很受宠的;几乎找不到一个不使用指针的C/C++应用。用于存储数据和程序的地址,这是指针的基本功能。用于指向整型数,用整数指针(int*);指向浮点数用浮点数指针(float*);指向结构,用对应的结构指针(struct
xxx *);指向任意地址,用无类型指针(void*)。 有时候,我们需要一些通用的指针。在C语言当中,(void*)
可以代表一切;但是在C++中,我们还有一些比较特殊的指针,无法用(void*)来表示。事实上,在C++中,想找到一个通用的指针,特别是通用的函数指针简直是一个“不可能任务”。
C++是一种静态类型的语言,类型安全在C++中举足轻重。在C语言中,你可以用void*来指向一切;但在C++中,void*并不能指向一切,就算能,也失去了类型安全的意义了。类型安全往往能帮我们找出程序中潜在的一些BUG。
下面我们来探讨一下,C++中如何存储各种类型数据的指针。 1. 数据指针
数据指针分为两种:常规数据指针和成员数据指针 1.1 常规数据指针
这个不用说明了,和C语言一样,定义、赋值是很简单明了的。常见的有:int*, double* 等等。 如:
int value = 123; int * pn = &value;
1.2
成员数据指针 有如下的结构:
struct MyStruct { int key; int value; };
现在有一个结构对象:
MyStruct me; MyStruct* pMe =
&me;
我们需要 value 成员的地址,我们可以:
int * pValue = &me.value; //或
int * pValue
= &pMe->value;
当然了,这个指针仍然是属于第一种范筹----常规数据指针。
好了,我们现在需要一种指针,它指向MyStruct中的任一数据成员,那么它应该是这样的子:
int MyStruct::* pMV = &MyStruct::value; //或 int MyStruct::* pMK = &MyStruct::key;
这种指针的用途是用于取得结构成员在结构内的地址。我们可以通过该指针来访问成员数据:
int value = pMe->*pMV; // 取得pMe的value成员数据。 int key
= me.*pMK; // 取得me的key成员数据。
那么,在什么场合下会使用到成员数据指针呢?
确实,成员指针本来就不是一种很常用的指针。不过,在某些时候还是很有用处的。我们先来看看下面的一个函数:
int sum(MyStruct* objs, int MyStruct::* pm, int count) { int result = 0; for(int i = 0; i < count; ++i) result += objs[i].*pm; return result; }
这个函数的功能是什么,你能看明白吗?它的功能就是,给定count个MyStruct结构的指针,计算出给定成员数据的总和。有点拗口对吧?看看下面的程序,你也许就明白了:
MyStruct me[10] = { {1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16},{17,18},{19,20} }; int sum_value = sum(me, &MyStruct::value, 10); //计算10个MyStruct结构的value成员的总和: sum_value 值 为 110 (2+4+6+8++20) int sum_key = sum(me, &MyStruct::key, 10); //计算10个MyStruct结构的key成员的总和: sum_key 值 为 100 (1+3+5+7++19)
也许,你觉得用常规指针也可以做到,而且更易懂。Ok,没问题:
int sum(MyStruct* objs, int count) { int result = 0; for(int i = 0; i < count; ++i) result += objs[i].value; return result; }
你是想这么做吗?但这么做,你只能计算value,如果要算key的话,你要多写一个函数。有多少个成员需要计算的话,你就要写多少个函数,多麻烦啊。
对于拷贝构造函数引用传递,似乎司空见惯,认为理所当然。但是被问起这个问题,的确是一片茫然,为什么呢? 去网上搜索了一下,的确有很多这方面的知识讲解。 我们先看一下CSDN上的一个帖子的回答: 简单的回答是为了防止递归引用。 具体一些可以这么讲: 当一个对象需要以值方式传递时,编译器会生成代码调用它的拷贝构造函数以生成一个复本。如果类A的拷贝构造函数是以值方式传递一个类A对象作为参数的话,当需要调用类A的拷贝构造函数时,需要以值方式传进一个A的对象作为实参; 而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导致又一次调用类A的拷贝构造函数,这就是一个无限递归。 这个解释还是蛮具体的。 利用值传递的话,会导致递归引用。 还有一片文章也谈到了这个问题,
我觉得写得也非常好! 为什么拷贝构造函数必须为引用传递,不能是值传递? 链接地址: http://www.cnblogs.com/chio/archive/2007/09/14/893299.html其中讲到了3个问题 1是拷贝构造函数的作用。
作用就是用来复制对象的,在使用这个对象的实例来初始化这个对象的一个新的实例。 2是参数传递过程到底发生了什么?
将地址传递和值传递统一起来,归根结底还是传递的是"值"(地址也是值,只不过通过它可以找到另一个值)! i)值传递: 对于内置数据类型的传递时,直接赋值拷贝给形参(注意形参是函数内局部变量);
对于类类型的传递时,需要首先调用该类的拷贝构造函数来初始化形参(局部对象);如void foo(class_type obj_local){},
如果调用foo(obj); 首先class_type
obj_local(obj) ,这样就定义了局部变量obj_local供函数内部使用 ii)引用传递: 无论对内置类型还是类类型,传递引用或指针最终都是传递的地址值!而地址总是指针类型(属于简单类型),
显然参数传递时,按简单类型的赋值拷贝,而不会有拷贝构造函数的调用(对于类类型). 3是在类中有指针数据成员时,拷贝构造函数的使用?
如果不显式声明拷贝构造函数的时候,编译器也会生成一个默认的拷贝构造函数,而且在一般的情况下运行的也很好。但是在遇到类有指针数据成员时就出现问题了:因为默认的拷贝构造函数是按成员拷贝构造,这导致了两个不同的指针(如ptr1=ptr2)指向了相同的内存。当一个实例销毁时,调用析构函数free(ptr1)释放了这段内存,那么剩下的一个实例的指针ptr2就无效了,在被销毁的时候free(ptr2)就会出现错误了,
这相当于重复释放一块内存两次。这种情况必须显式声明并实现自己的拷贝构造函数,来为新的实例的指针分配新的内存。 问题1和2回答了为什么拷贝构造函数使用值传递会产生无限递归调用的问题; 问题3回答了回答了在类中有指针数据成员时,拷贝构造函数使用值传递等于白显式定义了拷贝构造函数,因为默认的拷贝构造函数就是这么干的
一 拷贝构造函数是C++最基础的概念之一,大家自认为对拷贝构造函数了解么?请大家先回答一下三个问题:
1. 以下函数哪个是拷贝构造函数,为什么?
- X::X(const X&);
- X::X(X);
- X::X(X&, int a=1);
- X::X(X&, int a=1, b=2);
2. 一个类中可以存在多于一个的拷贝构造函数吗?
3. 写出以下程序段的输出结果, 并说明为什么?
如果你都能回答无误的话,那么你已经对拷贝构造函数有了相当的了解。
- #include
- #include
-
- struct X {
- template<typename T>
- X( T& ) { std::cout << "This is ctor." << std::endl; }
-
- template<typename T>
- X& operator=( T& ) { std::cout << "This is ctor." << std::endl; }
- };
-
- void main() {
- X a(5);
- X b(10.5);
- X c = a;
- c = b;
- }
解答如下:
1. 对于一个类X,如果一个构造函数的第一个参数是下列之一: a)
X& b) const X& c) volatile X& d) const
volatile X& 且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.
- X::X(const X&);
- X::X(X&, int=1);
2.类中可以存在超过一个拷贝构造函数,
- class X {
- public:
- X(const X&);
- X(X&);
- };
注意,如果一个类中只存在一个参数为X&的拷贝构造函数,那么就不能使用const
X或volatile X的对象实行拷贝初始化.
- class X {
- public:
- X();
- X(X&);
- };
-
- const X cx;
- X x = cx;
如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数. 这个默认的参数可能为X::X(const X&)或X::X(X&),由编译器根据上下文决定选择哪一个.
默认拷贝构造函数的行为如下: 默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造. 拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise
Copy)的动作. a)如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数. b)如果数据成员是一个数组,对数组的每一个执行按位拷贝.
c)如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值.
3. 拷贝构造函数不能由成员函数模版生成.
- struct X {
- template<typename T>
- X( const T& );
-
- template<typename T>
- operator=( const T& );
- };
-
原因很简单, 成员函数模版并不改变语言的规则,而语言的规则说,如果程序需要一个拷贝构造函数而你没有声明它,那么编译器会为你自动生成一个.
所以成员函数模版并不会阻止编译器生成拷贝构造函数, 赋值运算符重载也遵循同样的规则.(参见Effective C++ 3edition,
Item45)
二
针对上面作者的讨论,理解更深了,但是下面我还是会给出一个一般的标准的实现和注意事项:
#include "stdafx.h" #include "stdio.h" #include <iostream> #include <string>
struct Test1 { Test1() { } Test1(int i) { id = i; } Test1(const Test1& test) { id = test.id; } Test1& operator = (const Test1& test) { if(this == &test) return *this; id = test.id; return *this; } int id; };
class Test2 { public: Test2(){ m_pChar = NULL;} Test2(char *pChar) { m_pChar = pChar;} Test2(int num) { m_pChar = new char[num]; for(int i = 0; i< num; ++i) m_pChar[i] = 'a'; m_pChar[num-1] = '\0'; } Test2(const Test2& test) { char *pCharT = m_pChar;
m_pChar = new char[strlen(test.m_pChar)]; strcpy(m_pChar, test.m_pChar);
if(!pCharT) delete []pCharT; } Test2& operator = (const Test2& test) { if(this == &test) return *this;
char *pCharT = m_pChar; m_pChar = new char[strlen(test.m_pChar)]; strcpy(m_pChar, test.m_pChar);
if(!pCharT) delete []pCharT;
return *this; } private: char *m_pChar; };
int main(int argc, char* argv[]) { const Test1 ts(1); // Test1() const Test1* p_ts = &ts; const Test1 ts2(ts); //Test(const Test1& test) const Test1 ts3 = ts; //Test(const Test1& test) Test1 ts4; ts4 = ts; //Test1& operator = (const Test1& test)
Test2 t(5); Test2 t2(t); Test2 t3 = t2; Test2 t4; t4 = t; return 0; }
TAG:预编译和宏定义,VC++,VC++的预编译功能
TEXT:
这里介绍VC6的预编译功能的使用,由于预编译详细使用比较的复杂,这里只介绍几个最重要的预编译指令: /Yu,
/Yc,/Yx,/Fp。其它的详细资料可以参考: MSDN -> Visual Studio 6.0 Document -> Visual C++
6.0 Document -> VC++ Programmer Guider - >Compiler and Linker ->
Details -> Creating Precompiled Header files
预编译头的概念:
所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码,甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-
7M大。注意及时清理那些没有用的预编译头文件。
也许你会问:现在的编译器都有Time
stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg
Macro, Preprocessor )都要重新处理一遍。
VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。
根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。
要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)想必大家都知道
StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC
Dialog
Based 程序的预编译头文件。(因为AppWizard会为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们会发现这个头文件里包含了以下的头文件:
#include <afxwin.h> // MFC core and standard
components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation classes
#include <afxdtctl.h> // MFC support for Internet Explorer
4 Common Controls
#include <afxcmn.h>
这些正是使用MFC的必须包含的头文件,当然我们不太可能在我们的工程中修改这些头文件的,所以说他们是稳定的。
那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我们还需要一个cpp文件来生成.pch
文件。这个文件默认的就是StdAfx.cpp。在这个文件里只有一句代码就是:#include“Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已―――也就是说,要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指定StdAfx.cpp来生成一个.pch文件,通过/Fp编译开关来指定生成的pch文件的名字。打开project
- >Setting->C/C++ 对话框。把Category指向Precompiled
Header。在左边的树形视图里选择整个工程,Project Options(右下角的那个白的地方)可以看到 /Fp
“debug/PCH.pch”,这就是指定生成的.pch文件的名字,默认的通常是 <工程名>.pch。然后,在左边的树形视图里选择StdAfx.cpp,这时原来的Project
Option变成了 Source File Option(原来是工程,现在是一个文件,当然变了)。在这里我们可以看到
/Yc开关,/Yc的作用就是指定这个文件来创建一个Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件,一个工程里只能有一个文件的可以有YC开关。VC就根据这个选项把
StdAfx.cpp编译成一个Obj文件和一个PCH文件。
这样,我们就设置好了预编译头文件。也就是说,我们可以使用预编译头功能了。以下是注意事项:
1)如果使用了/Yu,就是说使用了预编译,我们在每个.cpp文件的最开头,包含你指定产生pch文件的.h文件(默认是stdafx.h)不然就会有问题。如果你没有包含这个文件,就告诉你Unexpected
file end.
2)如果你把pch文件不小心丢了,根据以上的分析,你只要让编译器生成一个pch文件就可以了。也就是说把
stdafx.cpp(即指定/Yc的那个cpp文件)重新编译一遍就可以了。
|