posts - 16,  comments - 34,  trackbacks - 0
共10页: First 2 3 4 5 6 7 8 9 10 
re: 一个有趣的小问题 OwnWaterloo 2009-09-01 21:37
@莫失莫忘
静态成员本来就可以通过
1. 类名
S::sum

2. 实例
S s;
s.sum;

两种形式访问, 毫无区别, 推荐第1种形式。


this->sum 依然是 S::sum, 而不会是s的非静态成员。
它根本就没有非静态的sum, 难道编译器还因为this->sum专门给它再加这么一个非静态sum么?
re: 一个有趣的小问题 OwnWaterloo 2009-09-01 21:31
@莫失莫忘
你是从那句解释中看出有解引用一说的?
re: VC6中的简易delegate实现 OwnWaterloo 2009-09-01 20:54
@李现民
如果楼主打算试试boost.function on vc6, 请告知下结果哦~~~
我对vc6吐出的错误有些恐惧~~~
re: VC6中的简易delegate实现 OwnWaterloo 2009-09-01 20:50
@李现民
boost.function有3种使用语法:

1.
function<void ()> ...
function<void (int) > ...
function<int (std::string,int) > ...

2.
function<void> ...
function<void,int> ...
function<int ,std::string,int > ...

3.
function0<void> ...
function1<void,int> ...
function2<int ,std::string,int > ...

越往后, 语法越丑陋, 但受更多编译器支持。

可以试试在vc6上是否支持boost.function的2使用方法(第1种就不要指望了~~~)
如果行, 就看boost是怎么做的。

我估计后一种语法的实现依然要偏特化, 所以不行。
那就只能使用最丑陋的第3种语法了。
@Kevin Lynx
我说说我的看法啊, 你看看是否合理。

将:
void OnE1(tuple<e11,...> );
void OnE2(tuple<e21,...> );
void OnE3(tuple<e31,...> );

转化为:
OnEvent(int e, any );

是合理的,因为这样做可以很好的适应"增加event种类"这一变化。
对"需要增加很多event"或者"event暂时未知"的情况, 是很有用的。
也算是降低了耦合度吧, 因为
>逻辑层也不需要改,只改UI的通知处理即可。
嗯……

相比一些其他的很严格的抽象方式, 比如将每个OnE1设置为纯虚…… 那简直是自讨苦吃……
一旦增加一个OnEx的纯虚, ui代码就需要改。
如果不是纯虚, 或者使用OnEvent(int e, any );就有一个"缓合"的机会, 可以慢慢来~~~



但是, 我有一个猜测, 你看看对不对:
OnEvent(int e, any );
添加这一层, 总工作量不会有丝毫减少, 甚至会增加。
有可能ui端会:
switch (e)

如果e的种类继续增加, 还可能在ui端继续转变为:
ReplyE1(tuple<E11, ... > );
ReplyE2(tuple<E21, ... > );
ReplyE3(tuple<E31, ... > );


再次申明啊, 我觉得OnEvent(int e, any );是有好处的。
logic和ui通过这个通信。
logic内部是否NotifyE1,NotifyE2的形式? UI是否采用RE1,RE2的形式?
这个是完全解耦了。
只是数据格式始终解不了。

>看过一些人直接在MFC的OnXXX里写一堆逻辑代码就觉得受不了。
嗯…… 我曾经就写过让你受不了的代码~~~
当然, 现在我也受不了那代码了……
但是呢, 现在又没机会去做这种logic,ui分离的事, 只是猜测OnEvent(int e, any );会使得总代码量增加。
希望能得到你的经验之谈~~~

光顾着说笑了…… 说点正事。
这样做算不上"降低耦合"吧?

void OnE1(tuple<e11,...> );
void OnE2(tuple<e21,...> );
void OnE3(tuple<e31,...> );

和:

void OnEvent(int e, any );

的耦合性不是完全一样么?
log和UI依然必须"协商"每个事件的参数是怎样的。


只是后一种方式, 将通信的接口纳入一个之中, 而不是每产生一个新事件就添加一个, 也就是说, 这个接口可以完全固定下来了, 不会再发生变化。

@Kevin Lynx
嗯,boost确实有点重……
那么我们抛弃boost::tuple, 使用std::tr1::tuple吧~~~

不过any…… 这可怜的家伙没被采纳…… 得自己实现……
嘿嘿嘿嘿~~~
设计来、设计去,回到了经典的WndProc模式:
typedef HRESULT (CALLBACK* WndProc)(HWND,UINT,WPARAM,LPARAM);


因为现在有模板这个高级货, 所以HWND,WPARAM,LPARAM可以塞到一起,变成:
typedef R (*OnNotify)(long type, xxx data);


然后呢, 为了更方便的将各种东西塞入data, lz创造了一个 Param.
试试boost.tuple?

为了让一个签名接受不同类型的tuple/Param, 再将它们塞入一个any/void*。


旧瓶新酒~~~
@megax
同意。 一旦any_cast出错,就是一个不能逃避,必须得修改掉的错误。
相对于void*, any能起到的作用只是开发时的debug?
发布时再改回无检查的void* ? 还要注意释放的问题……
re: 一个有趣的小问题 OwnWaterloo 2009-08-20 23:21
@胡友蒙
代码中只出现了无效地址, 但并没有解引用。

>而且很多时候都是致命的
这样做至少在i386上是100%不会出现任何问题, 所以, 不用说得这么吓人。
re: 一个有趣的小问题 OwnWaterloo 2009-08-20 22:34
(S)(*(sa+100*sizeof(S))).a();
这个表达式有2个小问题, 其他都到位。


由此, 可以给他解释另一个更经典的……
static_cast<S*>(0)->a();
-_-; ...

template<typename T>
class Obj;

template<typename T>
class Obj_iteator { ... };

template<typename T>
class Obj { ... };

这样吗?
re: 强大的bcb OwnWaterloo 2009-08-15 12:50
2和4都是符合标准的, 引用只能绑定到左值, 只有const引用才可以绑定到临时对象。
FileLoader::RawData FileLoader::GetRawData() const;
const FileLoader::RawData& r = loader.GetRawData();
// 估计不是所希望的, 应该这样才对:
const FileLoader::RawData v = loader.GetRawData();


void func( Obj &a )
func( Obj() ); // error in ISO C++

void func(const Obj& a);
func( Obj() ); // ok


1. 我只记函数模板中, 局部类可以使用模板参数。
反过来, 还真不记得了……
连同3, 这种使用了生僻模板技术的代码, 编写的时候就应该注意按标与各编译器的支持程度的差异。
@11
> 但是就这个应用来说根本用不到很大的精度

扯谈!

那可不可以说“就这个应用根本不需要考虑效率”?
使用多线程(线程池都免了)的效率完全足够, 并且——我一直强调的——它编码简单, 充分利用"OS已经实现的调度机制",而不是自己重复去发明一套弱小的调度功能。


一上来就抛出一句“典型滥用xxx”, 提出一个所谓的解决方案。
典型的“什么事都想靠自己解决”的初级程序员作法。
@11
@11
>Sleep(10); //10毫秒,可以根据需要的精度调整此检查频率
嘿嘿, 是不是我需要更高精度的时候, 应该Sleep(5); ???

你看看这个:
999...999 a_long_delay_work.exe

还觉得你那个方法“效率高”?
你想想"可以根据需要的精度调整此检查频率"这句话, 当需要的精度要求达到极限时, 比如Sleep(0); 那个while是不是一个"忙等"?
你以为Sleep没有开销吗? 还Sleep(10)呢, 哈哈哈。


你的代码简单吗? 全都是伪代码。
而且我也说了, OS调度里面, 这些代码都有, 你老人家何苦要去和它争呢?
就这Sleep(10) , 还争什么呢争……

你这方案, 至少得算出到下一个任务开始前, 应该大致Sleep多久, 它才不算一个忙等。

-------- --------
而且, 这方案还必须是“先得到所有任务”, 再跑这个半死循环, 才可以。
如果需要再跑半死循环的中途, 新建定时任务呢?
这需求很常见, 很多类似“记事簿”的软件都要提供这功能, 而且是随时插入新的任务。

显然, 你必须至少要开2个线程了, 1个用于用户输入, 1个用来跑半死循环。
“任务队列”现在被2线程共享, 又需要加锁来同步。
这就是我说的, 自己调度会造成编码复杂的原因, 懂?


别人的方案有何优势与劣势?
如此多的方案,它们各自何种情况下是最佳方式?
想过没? 同学?
不要想都不想——如果Sleep(10)就是你想的结果, 那我真没什么必要和你继续说下去了……——就随便扣个帽子, 你以为能显示出你很能吗?
@Stupid ET
这些代码是你高中写的?? 牛……

我觉得这思路挺好, 将n个任务分到不同线程, 然后用Sleep的方式通知OS。

谁知道OS里面会不会有:
“来一个排序, 以一个小的粒度检查时间来运行要运行的”线程
这种调度算法呢?

谁知道OS会不会比ls某位的算法更差呢?

谁知道windows下的创建线程开销会比ls某位的算法高呢?


这些都不知道, 要测也不方便。

但是, 编码简单很多~~~ 同时线程间的同步也很方便处理。
KISS~~~
@OwnWaterloo
哦, 原来是个backward compatibility函数……
高中生?
> Sleep(InputHandle()+2000); // 主线程暂停等待其他线程运行完毕
严格给的说, 要用 WaitForMultipleObjects。
如果人品非常差, 延迟2秒还是有主线程先结束的可能, 因为调度顺序是未知的。

> 因为调度未知
从CreateThread开始, 到次线程Sleep 之间会有未知时间的间隔。
所以, 传给次线程的, 不应该是delay, 而是awake_time。
次线程在Sleep前一刻, 根据awake_time和current_time, 计算Sleep多久。
这样, 精度就不受CreateThread,到Sleep之间那段未知时间的影响。
如果精度还要提高, 可以用WaitableTimer。

> CreateThread
在vc上用_beginthreadex代替。 dev c++如果是用的mingw, 也有这个函数。


最后一点………… 其实偷懒的话去查AT的用法 ……
run -> cmd -> at /?
直接写个bat文件:
at xxxx notepad
at yyyy calc
....

而不用写一个配置文件, 然后用一个程序去读, 再执行……
功能差不多, 精度可能低一些。
@pear_li
winexec怎么了?
re: FOX TOOLKIT 学习 - 文本显示 OwnWaterloo 2009-06-27 11:46
@visualfc

原来是要处理utf8...


我指的是
c89 stdlib.h
mbstowcs
wcstombs

c99 wchar.h
mbsrtowcs
wcsrtombs

转换跟locale有关, 标准只保证有"C"和""两种locale存在。
其他的也是跟实现相关。这样的话,上面4个函数的可移植优势就不大了。
同时msvc的crt还不支持utf8 ...


那就用iconv吧。。。 挺好的一个库。
为什么一定要找基于fox的方案呢?


另外,FXString 存放的总是utf8?
要将其显示在控件上?
将 utf8 转为 utf16 再用 xxxxW 函数应该就可以显示了。
应该不用转成 gbk 然后用 xxxxA 函数。
re: FOX TOOLKIT 学习 - 文本显示 OwnWaterloo 2009-06-25 10:54
>>在Windows平台上我使用了MultiByteToWideChar来实现代码页转换,如果谁能有基于FOX的解决方案,欢迎进行指点和帮助。

有基于C标准库的方案。

re: 查看源文件预处理结果 OwnWaterloo 2009-06-08 15:27
@zhichyu
应该跟源文件行号有关, 具体我不清楚
@万连文
微软的哪? MFC?

上面的命令行 cl /MD 已经告诉编译器链接到动态库了。
vc6不支持是因为它只有静态库。 crt的动态库是跟.net那一套东西一起出来的。
sorry…… 不小心提交了2次 ……
我自己删掉一个 ……
  1#include <stddef.h>
  2#include <stdlib.h>
  3#include <stdio.h>
  4#include <vector>
  5
  6#include <stdexcept>
  7
  8
  9#if defined(_MSC_VER) && _MSC_VER<1400
 10#include <new.h>
 11namespace std using ::set_new_handler; using ::new_handler; }
 12#else
 13#include <new>
 14#endif
 15
 16
 17void* allocate(size_t size) {
 18    if (size==0) size = 1;
 19    for (void* p=0;;) {
 20        p = malloc(size);
 21        if (p) {
 22            printf("allocate %p\n",p);
 23            return p;
 24        }

 25        std::new_handler handler = std::set_new_handler(0);
 26        std::set_new_handler(handler);
 27        if (handler)
 28            handler();
 29        else
 30            throw std::bad_alloc();
 31    }

 32}

 33
 34void deallocate(void* ptr) {
 35    printf("deallocate %p\n",ptr);
 36    free(ptr);
 37}

 38
 39void* operator new(size_t size) return allocate(size); }
 40void* operator new[](size_t size) return allocate(size); }
 41void operator delete(void* ptr) { deallocate(ptr); }
 42
 43class C {
 44    static int count;
 45public:
 46    static bool fail;
 47    C() {
 48        if (fail)
 49            throw std::exception();
 50        printf("C::C(),%d\n",++count);
 51    }

 52    ~C() {
 53        printf("C::~C(),%d\n",count--);
 54    }

 55    C(const C& ) {
 56        printf("C::(const C&),%d\n",++count);
 57    }

 58
 59
 60    //void* operator new(size_t,void* place) { return place; }
 61    void* operator new(size_t size) return allocate(size); }
 62    void* operator new[](size_t size) return allocate(size); }
 63    void operator delete(void* ptr) { deallocate(ptr); }
 64}
;
 65bool C::fail;
 66int C::count;
 67
 68struct S {
 69    static bool fail;
 70    S() {
 71        if (fail)
 72            throw std::exception();
 73        printf("construct\n");
 74    }

 75    ~S() {
 76        printf("destroy\n");
 77    }

 78}
;
 79bool S::fail;
 80
 81void test_class(int dim) {
 82    if (dim<=0)
 83        return;
 84    C::fail = dim==4;
 85    C* arr = new C[dim];
 86    delete[]  arr;
 87}

 88
 89
 90void test_global(int dim) {
 91    if (dim<=0)
 92        return;
 93    S::fail = dim==4;
 94    S* arr = new S[dim];
 95    delete[] arr;
 96}

 97
 98int main() {
 99    using namespace std;
100    int dim = 0;
101    for (printf("input dim: ");scanf("%d",&dim)==1;printf("input dim: "))
102    {
103        try {
104            test_class(dim);
105        }

106        catch (std::exception& ) {
107            printf("  ---- catch an exception ----\n");
108        }

109        try {
110            test_global(dim);
111        }

112        catch (std::exception& ) {
113            printf("  ---- catch an exception ----\n");
114        }

115    }

116}

117
 
有两个底层分配函数allocate和deallocate,它们使用malloc和free。
用这两个函数实现全局的3个op new,op new[], op delete,没有op delete[]
还用这两个函数实现了C的3个op new,op new[], op delete,同样没有op delete[]
用如下参数编译
cl /EHsc /MD /analyze /W3
 
你看看结果吧。
我用vc8、9测过(vc6不支持动态crt库,vc10我没装)。
反正两处delete[] arr;都没有调用 op delete。
它们调用那个全局的,没有被重写的op delete[]。
如果静态链接,该全局默认的op delete[]会被inline, 再调用该编译单元中定义的op delete。
如果动态链接,op delete[]不会被inline,会调用crt库中提供的op delete。
总之,这两处delete[] arr;都没有调用deallocate。
 
当然, 你可以说你只静态链接到crt库。
也可以说你的allocate和deallocate底层实现绝对会一直保持与vc提供的crt兼容
但是,你的代码的用户了解么?
难道你打算在文档中写“使用我的库的代码者,使用的crt库必须满足XXX要求,必须自己测试YYY”,只是为了你自己可以少写一个 op delete[]
这不是程序库开发者的态度。
 
还有两个小问题。
C* pc = static_cast<C*>(malloc(sizeof(*pc));
new (pc) C; // 编译错误
C* pc2 = new (std::nothrow) C; // 编译错误
 
当然, 你还是可以说你绝对不会用这种东西, 你是实用主义嘛。
但是你的库的使用者呢?
 
 
“出发点只是想找到一个经过验证的(大的、成功的产品使用过的)简便的工具”
你觉得这可以说明该产品中的每个细节都是无可挑剔的么?
越是大的产品,测试越不容易,更不容易暴露其中的问题,同时也许忽悠客户也更容易。
 
 
确实没有任何事物都是完美的,但不能连追求完美的心都舍弃了。
同时,从实用角度出发,让该库变得更完美,所付出的代价非常小,“按规则”办事就可以了,10来行代码的事,何乐而不为?
规则可以见《EffCpp》或者《C++CodingStandard》。
@万连文
某某牛库是哪个库?
其实跟有没有库这样做没关系,跟该库牛不牛也没什么关系。
不能跟着别人错。

底层实现是HeapXXX?
其实跟底层实现到底是什么没关系。
C++标准没要求底层实现怎么做, 但是要求重载operator new必须有其对应形式的operator delete重载。


跟测不测试也没关系。
一次不带TT不会怀上不等于次次都不会怀上。
一个编译器上测试成功,不等于每个编译器都能测试成功。
在某个编译器上能测试通过,并不能证明或证伪。


编写operator new/delete要遵守一些规范。
HeapAlloc能否处理 size=0?
HeapAlloc分配失败时的new_hanlder处理?
对其他形式new的隐藏?
"重载"(而不是覆盖全局标准)op new时,如果不提供相应形式的op delete,是否也能正确工作?
与其在不同平台,不同编译器,甚至是相同编译器的不同版本下进行测试,不如一开始就按照标准形式编写。
operator delete[]呢?
re: delete operator OwnWaterloo 2009-05-10 21:46
楼主可以参考这篇文章, 为什么delete不能像new那样使用。
http://cpp.ga-la.com/html/24/26/0707/288.htm

re: delete operator OwnWaterloo 2009-05-10 21:42
这样写是不正确的。

new操作符的含义是:
1. 调用相应的operator new分配内存
2. 在该内存上构造对象

delete 操作符执行相反的工作。
1. 调用对象的析构函数
2. 调用operator delete释放内存。

如果只调用operator delete, 对象析构函数将得不到调用。
re: 最简单的foreach实现(VC & GCC) OwnWaterloo 2009-05-08 12:46
同时…… 非常不明白 ……
为什么很多C++程序员(甚至是许多老手)都喜欢使用下划线开头的标识符……
这是个非常不好的习惯……
re: 最简单的foreach实现(VC & GCC) OwnWaterloo 2009-05-08 12:42
-------- -------- 引 -------- --------
这里有一个特殊的考虑,就是container有可能是一个临时对象,或者是某个函数的返回值。为了不对容器进行复制,利用了一个不太为人所知的C++特性,就是临时变量在存在引用时,生命期会由引用变量决定。这样保证在进行循环时始终有效。
-------- -------- 引 -------- --------


函数返回值就是临时对象的一种吧?
这个特性更准确描述应该是:
const 引用可以绑定到一个临时对象上,
临时对象的生命周期将延长至const引用超出作用域。
临时对象原本的生命周期仅到产生临时对象的完整表达式结束。


-------- -------- 分割线 -------- --------
是否可以考虑这样修改?
#define foreach(var, container) \
{ \
/... \
}

——1是可以避免临时对象的生命周期被延长到不需要的地方

——2是可以避免一个bug:
#define RANDOM_VAR(name, line) name ## line
这个宏不会以你想想中的样子工作。

至少要改成:
#define RANDOM_VAR(name, line) RANDOM_VAR_(name,line)
// 交给另一个宏
#define RANDOM_VAR_(name,line) name##line
// 才能启动rescan机制,将__LINE__展开


——3. 如果让foreach_helper有一个到container的const引用, 也就不需要单独的RANDOM_VAR去提升container的生命周期了。
Run-Time Check Failure #2 - Stack around the variable 'version' was corrupted。
这个错误信息描述不太准确。
其意思是,msvc在debug下为栈上自动变量周围分配了一些保护空间。
如果发生栈溢出, 就会写到保护空间上去。
这样, 栈溢出就能被检查出来, 并且告诉程序员, 某个变量附近的栈空间被破坏(腐坏?)了。


所以要重现这个错误只要这样:
int a[1212];
a[1986] = 0;

或者, 楼主的代码与下面更类似:
int i;
(&i)[ 1 ] = 0; // 取i的地址, 将其作为数组传递给fread。
// 但其实这个“数组”, 只有一个元素。 也会发生溢出

要修改, 要么一次只读取一个version。
要么将version改到合适大小(10就能保证一定不溢出吗?)


re: WCHAR我不再惧怕了 OwnWaterloo 2009-04-28 22:40
@shongbee2

#include <locale>

wcout<<L"汉字";
assert( !wcout );

wcout.imbue(locale("chs"));
wcout.clear();
wcout<<L"汉字";

re: 过来开个坑 OwnWaterloo 2009-04-25 16:10
终于游说成功 ……
哪不方便? 网易我没写过……
不过不支持评论rss或者通知比较不爽。。。
@vitacy

1. C标准对默认实际参数提升规则有明确定。
也就是说, 带有可变长参数列表的函数, 绝对不会接受到char类型的实际参数。

2. C标准对va_arg是否自动对齐没有任何说明
你说的va_arg(va_list,type)是自动对齐, 只是在你的编译器上。
并不是所有编译器都能自动帮你完成这个工作。

在所有C实现上, 能保证第1点, 但不能保证第2点。
依赖于第2点, 代码就依赖于特定编译器。


你说va_arg(ap,type)是自动对齐, 证明你有研究过。
喜欢作这些研究的, 都是聪明的家伙。
但聪明的家伙总喜欢不按规矩办事


在gcc (GCC) 3.4.2 (mingw-special)中, type使用char, 会得到严重的警告:
`char' is promoted to `int' when passed through `...'
(so you should pass `int' not `char' to `va_arg')
note: if this code is reached, the program will abort
它会直接挂掉你的程序,来约束你必须按规矩办事。
re: 查看源文件预处理结果 OwnWaterloo 2009-04-21 14:45
将需要处理掉的那部分单独copy出来可以吗?
re: std::endl 是什么? OwnWaterloo 2009-04-18 20:58
class slt_like_ostream {

slt_like_ostream&
operator<<(std::ios_base& (*pf)(std::ios_base& );

template< class C, class T >
slt_like_ostream&
operator<<(std::basic_ios< C,T>& (*pf)(std::basic_ios< C, T>&);

template< class C, class T >
operator<<
(std::basic_ostream< C,T>& (*pf)(std::basic_ostream< C,T>&);

};

这是stl对ostream的规范。
如果不实现这些规范, 不能与stl的其他组件(如std::endl)合作也是理所当然的。

re: 自己实现的memcpy OwnWaterloo 2009-04-18 20:27
还发现一个问题……
void* my_memcpy(void *dst, /* const */ void* src, int size);
re: 自己实现的memcpy OwnWaterloo 2009-04-18 19:08
re: 自己实现的memcpy OwnWaterloo 2009-04-18 15:30
没有考虑内存对齐。
指针算术
简单的说, 假设你想查看前1212个元素,应该写 :
test.element, 1212。

T arr[sz];

watch中写arr。 watch会将arr认为是数组, 并且可以让你展开,查看特定元素。


T* p;
watch中写 p。 watch会将p认为是指针, 只能展开1项, 查看其值。
watch中写 p,n。 watch将认为p是一个数组的首地址, 并为你展开前n项。


re: istringstream类研究 OwnWaterloo 2009-03-25 18:48
@abilitytao
那直接就有 iostream streambuf exception stdexcept 这些文件咯?
它们的文件名都超过8个字符。

我这里没有…… 所以我怀疑是vc6 for dos……

我也有vs2008 …… 不过还有固执的人在使用vc6 …… 所以尽可能的提供支持……
re: istringstream类研究 OwnWaterloo 2009-03-25 13:20
@abilitytao
sorry, 刚才语气有点过激。
希望楼主能解答我的问题, 再次感谢~~
re: istringstream类研究 OwnWaterloo 2009-03-25 12:31
@abilitytao
我还不知道iostream属于标准库吗?
我只是想问下楼主的vc6的版本?
Microsoft Visual Studio\VC98\Include下的文件是大写还是小写?
有没有8字符限制?
谢谢~

re: istringstream类研究 OwnWaterloo 2009-03-24 23:46
以上程序在vc6.0上运行正常?

我这里的vc6, 会报很多头文件缺失。
比如iostream 会缺少streambuf stdexcept exception

要自己写这3个文件, 分别再include STREAMBF STDXCEPT XCEPTION

总之, 我这里的vc6的include中的头文件, 名字全是大写,而且都不超过8个字符。
我怀疑我用的是不是vc6 for dos 。。。。
re: 对malloc的返回值应该如何转型 OwnWaterloo 2009-03-17 22:56
@C++ Beginner
如果一个用户自定义类型C,
有一个可以通过单个类型为T的参数进行调用的构造函数,
那么,该构造函数就定义了一个T到C的转换。

如果该构造函数是explicit的,T到C的转换是显式转换。
如果该构造函数不是explicit,T到C的转换是隐式转换。

例如:
class string {
char* content_;
explicit string(const char* source,int length=-1)
:content_(0)
{
if (length==-1)
length = static_cast<int>(strlen(source));
content_ = new char[length+1];
strcpy(source,content_);
}
};

这个构造函数可以通过单个参数调用(第2个参数有默认值),所以这个构造函数定义了一个 const char* 到 string 的转换。

如果有explicit,那么该转换必须是显式转换:
string s = static_cast<string>("hello");

如果没有explict,该转换可以是隐式转换:
string s = "hello";


上面说错了,这种转换确实会调用构造函数。
共10页: First 2 3 4 5 6 7 8 9 10 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(8)

随笔档案(16)

链接

搜索

  •  

积分与排名

  • 积分 - 196688
  • 排名 - 132

最新随笔

最新评论

阅读排行榜

评论排行榜