关于友元和单利模式

combobox2013 2013-06-08 08:26:52
再次发帖讨论单例模式在c++中的实现


以下是一个博客里的文章


http://www.cnblogs.com/baiyanhuang/archive/2009/09/16/1730741.html

而C++中由于提供了友元这个特性,实现起来要好一些:

// Singleton base class, each class need to be a singleton should
// derived from this class


template <class T> class Singleton
{
protected:
Singleton(){}
public:
static T& Instance()
{
static T instance;
return instance;
}
};

// Concrete singleton class, derived from Singleton<T>
class ExampleSingleton: public Singleton<ExampleSingleton>
{
// so that Singleton<ExampleSingleton> can access the
// protected constructor
friend class Singleton<ExampleSingleton>;

protected:
ExampleSingleton(){}
public:
// This class's real functionalities
void Write(){printf("Hello, World!");}
};

// use this singleton class
ExampleSingleton::Instance().Write();

在C++友元的帮助下,我们成功实现了在编译期保证实例的唯一性。(当然,前提是你不要"乱交朋友")。

有人可能会问,实现singleton的代码并不多,我们没必要搞这么一个机制来做代码复用吧? 的确,我们复用的代码并不是很多,但是,我想代码复用的目的不仅仅是减少代码量,其最重要的目的还是在于保持行为的一致性,以便于使用与维护。(用函数替换代码段便是一个很好的例子)。
对于这里的singleton类来讲,如果不做这个设计,我们在每个具体的singleton类内部实现其singleton机制,那么可能出现的问题是
1. 很难保证其接口的一致性
张三写了一个singleton类,全局访问函数是Instance, 李四也写了一个Singleton类,全局访问函数可能就是GetInstance了。。。。。我们并没有一个健壮的机制来保证接口的一致性,从而导致了使用的混乱性。

2. 不易维护
Singleton创建实例有两种:一种为lazy initialization, 一种就是early initialization, 假如开始的实现是所有的singleton都用lazy initialization, 突然某种需求要求你用early initialization,你的噩梦就开始了,你不得不重写每一个singleton类。

而用了singleton模板基类这种机制,以上问题就不会存在,我们得到的不仅仅是节约几行代码:)




问题1:
这里用了奇异递归模式, 我的问题是:奇异递归在这里有什么意义吗? 难道是作者无意的写法,导致我想多了?

问题2: 父类难道也要 作为友元,才能访问 派生类的构造函数吗?

问题3:

关于lazy 和early的问题, 对于以上的例子的写法,属于哪一种模式,为什么?我实在搞不清,

问题有些多,谢谢大家。

...全文
939 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
我看你有戏 2013-06-21
  • 打赏
  • 举报
回复
这个单例写得真巧妙,太经典了
combobox2013 2013-06-20
  • 打赏
  • 举报
回复
引用 19 楼 lm_whales 的回复:
[quote=引用 18 楼 wangdahu888 的回复:] [quote=引用 16 楼 combobox2013 的回复:] 这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!!
很明显,你这种说法是错误的,汇编说明一切,看一下反汇编的代码吧: Test<int> obj1; obj1.Fun(); 0041147E lea ecx,[obj1] 00411481 call Test<int>::Fun (4111CCh) Test<int>obj2; obj2.Fun(); 00411486 lea ecx,[obj2] 00411489 call Test<int>::Fun (4111CCh) 这2个 对象的Fun函数都在同一地址,明显是同一个函数. [/quote] 在同一处编译,只会有一个实例化的版本,但是在不同地方编译,可以出现多份实例 另外,有些编译器可能有办法解决这个问题。 同一程序,编译成多个.obj或者.lib 这是很正常的现象; 然后把这些.obj,.lib链接成一个程序。 这样就会出现一个模板有两份实现的现象了。 [/quote] 你的 答案 ,我请教了几个前辈了 他们说,如果特殊情况下,递归定义也会出现问题
橡木疙瘩 2013-06-18
  • 打赏
  • 举报
回复
发现最后一次调用忘记改了。修改之后再运行一次:

//主程序:
#include <iostream>
#include <string>

template< typename T>
class Tester
{
public:
    T Member();
    virtual T VirtualMember();
    static T StaticMember();
};
Tester<std::string> * GetTester1();
Tester<std::string> * GetTester2();
void Test1();
void Test2();

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "\nCall Test1:\n";
    Test1();

    std::cout << "\nCall Test2:\n";
    Test2();

    std::cout << "\nCall GetTester1:\n";
    Tester<std::string> * t = GetTester1();
    t->Member();
    t->VirtualMember();
    t->StaticMember();

    std::cout << "\nCall GetTester2:\n";
    t = GetTester2();
    t->Member();
    t->VirtualMember();
    t->StaticMember();


    return 0;
}

//运行结果:

Call Test1:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call Test2:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call GetTester1:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call GetTester2:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

橡木疙瘩 2013-06-18
  • 打赏
  • 举报
回复
说到模板实例,还有一个很有趣的现象:

//test1.cpp:
#include <iostream>
#include <string>

template< typename T>
class Tester
{
public:
    T Member();
    virtual T VirtualMember();
    static T StaticMember();
};

template <typename T>
T Tester<T>::Member()
{
    static T nothing;
    std::cout << "Tester::Member() in Test1.cpp\n";
    return nothing;
}
template <typename T>
T Tester<T>::VirtualMember()
{
    static T nothing;
    std::cout << "Tester::Member() in Test1.cpp\n";
    return nothing;
}
template <typename T>
T Tester<T>::StaticMember()
{
    static T nothing;
    std::cout << "Tester::Member() in Test1.cpp\n";
    return nothing;
}

Tester<std::string> * GetTester1()
{
    return new Tester<std::string>;
}

void Test1()
{
    Tester<std::string> t;
    t.Member();
    t.VirtualMember();
    t.StaticMember();
}

//Test2.cpp
#include <iostream>
#include <string>

template< typename T>
class Tester
{
public:
    T Member();
    virtual T VirtualMember();
    static T StaticMember();
};

template <typename T>
T Tester<T>::Member()
{
    static T nothing;
    std::cout << "Tester::Member() in Test2.cpp\n";
    return nothing;
}
template <typename T>
T Tester<T>::VirtualMember()
{
    static T nothing;
    std::cout << "Tester::Member() in Test2.cpp\n";
    return nothing;
}
template <typename T>
T Tester<T>::StaticMember()
{
    static T nothing;
    std::cout << "Tester::Member() in Test2.cpp\n";
    return nothing;
}

Tester<std::string> * GetTester2()
{
    return new Tester<std::string>;
}

void Test2()
{
    Tester<std::string> t;
    t.Member();
    t.VirtualMember();
    t.StaticMember();
}

//主程序:
#include <iostream>
#include <string>

template< typename T>
class Tester
{
public:
    T Member();
    virtual T VirtualMember();
    static T StaticMember();
};
Tester<std::string> * GetTester1();
Tester<std::string> * GetTester2();
void Test1();
void Test2();

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "\nCall Test1:\n";
    Test1();

    std::cout << "\nCall Test2:\n";
    Test2();

    std::cout << "\nCall GetTester1:\n";
    Tester<std::string> * t = GetTester1();
    t->Member();
    t->VirtualMember();
    t->StaticMember();

    std::cout << "\nCall GetTester1:\n";
    t = GetTester1();
    t->Member();
    t->VirtualMember();
    t->StaticMember();


	return 0;
}

//运行结果:

Call Test1:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call Test2:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call GetTester1:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

Call GetTester1:
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp
Tester::Member() in Test1.cpp

所以,大家如果要写一个只在某一个cpp文件中使用的“辅助类”,一定不要忘记把它们放在匿名namespace中,不要以为只放在cpp中就不会被别人使用,要当心因为重名而被合并。上面的代码中,如果在test1.cpp和test2.cpp中把类声明及定义都放在匿名namespace中,就会出现链接错误。
橡木疙瘩 2013-06-18
  • 打赏
  • 举报
回复
引用 15 楼 lm_whales 的回复:
C++的静态变量或者静态函数,和模板一起,就会让单例模式失去作用,因为可以在每个实现的地方实例化模板; 那么静态变量或者静态函数,就会在同一个程序里有又多个版本,因为静态变量或者静态函数是内部连接的,同一程序可以出现多个同名变量或者函数假设这个程序分成N个部分编译,每一份都分别实例化模板的话,就会有N个单例的实例,关键是模板是使用时实例化的,除非预先实例化,并编译好模板,否则,就会出现多份的单例的实例。 所以就会出现这种递归定义的情况,用来解决这个问题。
老编译器可能会出现这种问题,但符合标准的编译器会处理这个问题。目前多数编译器会在每个调用该模板的cpp文件中为函数生成一份副本,但在链接时会把相同实例的函数代码合并,最终代码中每个实例只有一个版本。并且,由于静态局部变量的存在,编译器将不会对它进行inline化。 一个类模板的实例就是一个类,其任何行为都和一个类没有任何区别。 要注意类的静态成员与C的全局静态变量/全局静态函数是完全不一样的,C的全局静态标识符是模块内部连接的,会在每个使用它的模块中生成一个副本,但类的静态成员不是这个语义。事实上,虽然C++中依然支持全局静态变量/函数,但只是为了保持对旧代码的兼容,标准中并不提倡继续使用这一特性,而是建议使用匿名namespace代替它。因些,如果某个编译器把全局静态函数的所有副本合并成了一个,也是很有可能出现的情况。
  • 打赏
  • 举报
回复
引用 19 楼 lm_whales 的回复:
[quote=引用 18 楼 wangdahu888 的回复:] [quote=引用 16 楼 combobox2013 的回复:] 这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!!
很明显,你这种说法是错误的,汇编说明一切,看一下反汇编的代码吧: Test<int> obj1; obj1.Fun(); 0041147E lea ecx,[obj1] 00411481 call Test<int>::Fun (4111CCh) Test<int>obj2; obj2.Fun(); 00411486 lea ecx,[obj2] 00411489 call Test<int>::Fun (4111CCh) 这2个 对象的Fun函数都在同一地址,明显是同一个函数. [/quote] 在同一处编译,只会有一个实例化的版本,但是在不同地方编译,可以出现多份实例 另外,有些编译器可能有办法解决这个问题。 同一程序,编译成多个.obj或者.lib 这是很正常的现象; 然后把这些.obj,.lib链接成一个程序。 这样就会出现一个模板有两份实现的现象了。 [/quote] 我试了下,在vs2005中,在不同的的编译单元中,也是一份,我让朋友在gcc4.3中试了下,也一样

#pragma once
#include <stdio.h>
template<typename T>
class Test
{
public:
	static T val;
public:
	static  void Fun(){}
	static  void show(){
		printf("%x\n",&val);
	}
	void Fun2(){}
};
template<typename T>
T Test<T>::val=1
放在不同的文件中编译,调用,打印出的地址,都是一样,
lm_whales 2013-06-18
  • 打赏
  • 举报
回复
引用 18 楼 wangdahu888 的回复:
[quote=引用 16 楼 combobox2013 的回复:] 这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!!
很明显,你这种说法是错误的,汇编说明一切,看一下反汇编的代码吧: Test<int> obj1; obj1.Fun(); 0041147E lea ecx,[obj1] 00411481 call Test<int>::Fun (4111CCh) Test<int>obj2; obj2.Fun(); 00411486 lea ecx,[obj2] 00411489 call Test<int>::Fun (4111CCh) 这2个 对象的Fun函数都在同一地址,明显是同一个函数. [/quote] 在同一处编译,只会有一个实例化的版本,但是在不同地方编译,可以出现多份实例 另外,有些编译器可能有办法解决这个问题。 同一程序,编译成多个.obj或者.lib 这是很正常的现象; 然后把这些.obj,.lib链接成一个程序。 这样就会出现一个模板有两份实现的现象了。
  • 打赏
  • 举报
回复
引用 16 楼 combobox2013 的回复:
这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!!
很明显,你这种说法是错误的,汇编说明一切,看一下反汇编的代码吧: Test<int> obj1; obj1.Fun(); 0041147E lea ecx,[obj1] 00411481 call Test<int>::Fun (4111CCh) Test<int>obj2; obj2.Fun(); 00411486 lea ecx,[obj2] 00411489 call Test<int>::Fun (4111CCh) 这2个 对象的Fun函数都在同一地址,明显是同一个函数.
rocktyt 2013-06-18
  • 打赏
  • 举报
回复
引用 16 楼 combobox2013 的回复:
几天前问的问题,问哦当时没有看懂, Singleton<ExampleSingleton>可以有多个, 如Singleton<ExampleSingleton> obj1; Singleton<ExampleSingleton>obj2; ob1.getInstacne(); 此时会产生一个 ob1.getInstacne(); 此时会产生第2 个ExampleSingleton. 原因见15楼总结: [quote=引用 15 楼 lm_whales 的回复:] C++的静态变量或者静态函数,和模板一起,就会让单例模式失去作用,因为可以在每个实现的地方实例化模板; 那么静态变量或者静态函数,就会在同一个程序里有又多个版本,因为静态变量或者静态函数是内部连接的,同一程序可以出现多个同名变量或者函数假设这个程序分成N个部分编译,每一份都分别实例化模板的话,就会有N个单例的实例,关键是模板是使用时实例化的,除非预先实例化,并编译好模板,否则,就会出现多份的单例的实例。 所以就会出现这种递归定义的情况,用来解决这个问题。
这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!! [/quote]你所谓的 这2个 对象的Fun函数是不同的!!!! 请问你是怎么判断的?
combobox2013 2013-06-17
  • 打赏
  • 举报
回复
引用 12 楼 u010936098 的回复:
Singleton<ExampleSingleton>的构造函数是protected的,一般正常使用的话只会有一个实例,来自静态方法Instance,是这个静态方法的静态局部变量,真实类型为其衍生类ExampleSingleton。 如果想要出现多个Singleton<ExampleSingleton>的实例或是ExampleSingleton的多个实例也很简单:在ExampleSingleton的成员函数里面可以创建多个。 此外,在其它代码中也很容易创建多个Singleton<ExampleSingleton>或是ExampleSingleton的实例,因为这两个类都没有声明拷贝构造函数,编译器会自动生成一个并且是public的:

    ExampleSingleton second( ExampleSingleton::Instance());
    Singleton<ExampleSingleton> third(ExampleSingleton::Instance());
因此,应该在Singleton的声明中加上:

private:
    Singleton( Singleton<T> const & );
高手还在?
combobox2013 2013-06-17
  • 打赏
  • 举报
回复
几天前问的问题,问哦当时没有看懂, Singleton<ExampleSingleton>可以有多个, 如Singleton<ExampleSingleton> obj1; Singleton<ExampleSingleton>obj2; ob1.getInstacne(); 此时会产生一个 ob1.getInstacne(); 此时会产生第2 个ExampleSingleton. 原因见15楼总结:
引用 15 楼 lm_whales 的回复:
C++的静态变量或者静态函数,和模板一起,就会让单例模式失去作用,因为可以在每个实现的地方实例化模板; 那么静态变量或者静态函数,就会在同一个程序里有又多个版本,因为静态变量或者静态函数是内部连接的,同一程序可以出现多个同名变量或者函数假设这个程序分成N个部分编译,每一份都分别实例化模板的话,就会有N个单例的实例,关键是模板是使用时实例化的,除非预先实例化,并编译好模板,否则,就会出现多份的单例的实例。 所以就会出现这种递归定义的情况,用来解决这个问题。
这楼的答案,我看到后,我特意去搜了一下类模版方面的资料。 发现: template<typename T> class Test { T val; public: void Fun(){} }; Test<int> obj1; obj1.Fun(); Test<int>obj2; obj2.Fun(); 这2个 对象的Fun函数是不同的!!!! 尽管实例化的时候都是int型!!
lm_whales 2013-06-17
  • 打赏
  • 举报
回复
C++的静态变量或者静态函数,和模板一起,就会让单例模式失去作用,因为可以在每个实现的地方实例化模板; 那么静态变量或者静态函数,就会在同一个程序里有又多个版本,因为静态变量或者静态函数是内部连接的,同一程序可以出现多个同名变量或者函数假设这个程序分成N个部分编译,每一份都分别实例化模板的话,就会有N个单例的实例,关键是模板是使用时实例化的,除非预先实例化,并编译好模板,否则,就会出现多份的单例的实例。 所以就会出现这种递归定义的情况,用来解决这个问题。
combobox2013 2013-06-16
  • 打赏
  • 举报
回复
引用 12 楼 u010936098 的回复:
Singleton<ExampleSingleton>的构造函数是protected的,一般正常使用的话只会有一个实例,来自静态方法Instance,是这个静态方法的静态局部变量,真实类型为其衍生类ExampleSingleton。 如果想要出现多个Singleton<ExampleSingleton>的实例或是ExampleSingleton的多个实例也很简单:在ExampleSingleton的成员函数里面可以创建多个。 此外,在其它代码中也很容易创建多个Singleton<ExampleSingleton>或是ExampleSingleton的实例,因为这两个类都没有声明拷贝构造函数,编译器会自动生成一个并且是public的:

    ExampleSingleton second( ExampleSingleton::Instance());
    Singleton<ExampleSingleton> third(ExampleSingleton::Instance());
因此,应该在Singleton的声明中加上:

private:
    Singleton( Singleton<T> const & );
引用 12 楼 u010936098 的回复:
Singleton<ExampleSingleton>的构造函数是protected的,一般正常使用的话只会有一个实例,来自静态方法Instance,是这个静态方法的静态局部变量,真实类型为其衍生类ExampleSingleton。 如果想要出现多个Singleton<ExampleSingleton>的实例或是ExampleSingleton的多个实例也很简单:在ExampleSingleton的成员函数里面可以创建多个。 此外,在其它代码中也很容易创建多个Singleton<ExampleSingleton>或是ExampleSingleton的实例,因为这两个类都没有声明拷贝构造函数,编译器会自动生成一个并且是public的:

    ExampleSingleton second( ExampleSingleton::Instance());
    Singleton<ExampleSingleton> third(ExampleSingleton::Instance());
因此,应该在Singleton的声明中加上:

private:
    Singleton( Singleton<T> const & );
多谢大侠赐教, 剩余一个小问题,第9楼,能否帮忙再看看 我也好结贴 多谢
橡木疙瘩 2013-06-15
  • 打赏
  • 举报
回复
Singleton<ExampleSingleton>的构造函数是protected的,一般正常使用的话只会有一个实例,来自静态方法Instance,是这个静态方法的静态局部变量,真实类型为其衍生类ExampleSingleton。 如果想要出现多个Singleton<ExampleSingleton>的实例或是ExampleSingleton的多个实例也很简单:在ExampleSingleton的成员函数里面可以创建多个。 此外,在其它代码中也很容易创建多个Singleton<ExampleSingleton>或是ExampleSingleton的实例,因为这两个类都没有声明拷贝构造函数,编译器会自动生成一个并且是public的:

    ExampleSingleton second( ExampleSingleton::Instance());
    Singleton<ExampleSingleton> third(ExampleSingleton::Instance());
因此,应该在Singleton的声明中加上:

private:
    Singleton( Singleton<T> const & );
combobox2013 2013-06-13
  • 打赏
  • 举报
回复
引用 9 楼 combobox2013 的回复:
[quote=引用 8 楼 lm_whales 的回复:] Singleton<ExampleSingleton>可以有许多分呀
每次都从 getinstance里获取, 怎么能获取到多份呢? 不可能吧。 理解不了啊 [/quote] ??? 还有高手可以解答吗?
漫步者、 2013-06-09
  • 打赏
  • 举报
回复
很简单,保证了实例的唯一性,通过模板参数来获得的。
lm_whales 2013-06-09
  • 打赏
  • 举报
回复
class ExampleSingleton //: public Singleton<ExampleSingleton> { // so that Singleton<ExampleSingleton> can access the // protected constructor friend class Singleton<ExampleSingleton>; protected: ExampleSingleton(){} public: // This class's real functionalities void Write(){printf("Hello, World!");} }; Singleton<ExampleSingleton>::Instance().Write(); 每一个使用Singleton<ExampleSingleton>的地方都可能会生成一个Instance()因为Singleton<ExampleSingleton>和ExampleSingleton毫无关系; class ExampleSingleton: public Singleton<ExampleSingleton> { // so that Singleton<ExampleSingleton> can access the // protected constructor friend class Singleton<ExampleSingleton>; protected: ExampleSingleton(){} public: // This class's real functionalities void Write(){printf("Hello, World!");} }; 这里只会生成一个ExampleSingleton::Instance(); 因为Instance()是属于ExampleSingleton的(继承)
十八道胡同 2013-06-09
  • 打赏
  • 举报
回复
问题3: 难道early和 lazy的区别是: 静态对象和new对象的 区别? 例子里是返回一个static 对象 区别主要是何时见对象的问题。 2中都可以用静态对象来实现的,。
combobox2013 2013-06-09
  • 打赏
  • 举报
回复
引用 1 楼 u010936098 的回复:
singleton使用ExampleSingleton作为模板参数是为了生成ExampleSingleton& Instance()这个成员函数,没有模板参数的话,无法正确声明Instance的返回值类型。然后ExampleSingleton从singleton<ExampleSingleton>派生,从而继承了那个正确声明的Instance成员函数。 proctected成员可以被自身和它的派生类代码访问,其它类无法访问,基类(或称为父类)也没有特权。 这个例子应该是lazy模式,因为对象将在Instance函数第一次被调用时创建。如果程序运行过程中Instance没有被调用过,则不会创建这个对象。
引用 2 楼 ganpengjin1 的回复:
很简单,保证了实例的唯一性,通过模板参数来获得的。
对于问题1,我提供一个写法,供参考: class ExampleSingleton //: public Singleton<ExampleSingleton> { // so that Singleton<ExampleSingleton> can access the // protected constructor friend class Singleton<ExampleSingleton>; protected: ExampleSingleton(){} public: // This class's real functionalities void Write(){printf("Hello, World!");} }; 看我如使用之: Singleton<ExampleSingleton>::Instance().Write(); //第一步获取到对象,第二步调用成员函数. 当然这个写法貌似复杂,因为 没采用继承, 所以 ExamppleSingleton 没有Instacnce这个函数。 问题3: 难道early和 lazy的区别是: 静态对象和new对象的 区别? 例子里是返回一个static 对象
combobox2013 2013-06-09
  • 打赏
  • 举报
回复
加载更多回复(5)

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧