类与命名空间
来源:互联网 发布:mac 安装hbuilder 编辑:程序博客网 时间:2024/05/17 17:58
类与命名空间
类与命名空间
<<C++primer>>12.1.4中讲到:
在一个给定的源文件中,一个类只能被定义一次。如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的。
解析:定义一个类,指的是定义同一个类。如果名字相同,实现不同,那属于不同的类。
由此我们容易想到以下问题:
(1) 此处类的定义指什么?若是普通变量的定义,那是不可以在多个文件中同时定义的,只能在一处定义,多处声明,否则会发生重定义。但为何类的定义却可以,值得斟酌。
(2) 如果实现不同,名字相同会出现什么情况呢?
(3) 用include和不用include又有何不同?
(4) 头文件为.h和不为.h又如何呢?
1 实例一
- <span style="font-size:18px;">
- //car1.h
- #include<iostream>
- class car{
- public:
- car(){std::cout<<"car1:"<<std::endl;}
- car(int v,intc):value(v),capacity(c)
- {std::cout<<"car1:"<<std::endl;}
- void show()
- {
- std::cout<<"show car1:"<<std::endl;
- std::cout<<"value:"<<value<<"
- capacity:"<<capacity<<std::endl;
- }
- int GetV(){return value;}
- private:
- int value;
- int capacity;
- };</span>
//car2.h
- <span style="font-size:18px;">
- #include<iostream>
- class car{
- public:
- car(){std::cout<<"car1:"<<std::endl;}
- car(int v,intc):value(v),capacity(c)
- {std::cout<<"car1:"<<std::endl;}
- void show()
- {
- std::cout<<"show car1:"<<std::endl;
- std::cout<<"value:"<<value<<"
- capacity:"<<capacity<<std::endl;
- }
- int GetV(){return value;}
- private:
- int value;
- int capacity;
- };</span>
//namespace.cpp
#include"car1.h"
#include"car2.h"
//很奇怪,没有出现重定义,我们可以猜测其实这里说的类定义其实就是类似于结构体声明。多次声明不会出现任何问题。只是c才是变量的实例化。声明类并不分配实实在在的空间。
- <span style="font-size:18px;">
- int main()
- {
- car c(100,10);
- c.show();
- std::cout<<c.GetV()<<std::endl;
- return 0;
- }</span>
由以上的小测试程序我们基本可以得出:类的定义实际上是一种声明活动,并没有空间的分配。
上面头文件中只是类里有内联函数,我们知道内联函数定义在头文件不会引起重定义,那么我们试着实现一个非内联函数:
- <span style="font-size:18px;">
- int GetC();//在类内部的声明
- int car::GetC()
- {
- return capacity;
- }</span>
结果我们惊奇的发现,结果仍然正常运行。这是否说明类的成员函数也必须在实例化对象后才会实例化呢,这样每一个对象会维护一个成员函数的副本?
为了验证这个问题,我们用一个静态函数来测试:
- <span style="font-size:18px;">
- static void test();
- void car::test()
- {
- std::cout<<“for test!“<<std::endl;
- }</span>
结果仍然好使,这说明类的整个定义过程都视为声明过程,不会出现重定义错误。
保险起见,我们在.h中再尝试一个全局函数:
- <span style="font-size:18px;">
- void test2()
- {
- std::cout<<"outer Func!"<<std::endl;
- }</span>
结果仍然编译运行正常,无语真把我逼急了,声明一个全局变量int a=10;结果还是正常,但是如果两次包含carx.h(x=1 or 2)就会出现重定义错误。后来尝试把car2.h中的输出部分car1字符串改为car2,发现car2.h覆盖了car1.h。这是什么原因呢,是C++对C头文件兼容的一些额外处理吗?不懂
我们再来测试一下C++ 标准头文件格式(直接把后缀名.h去掉即可):结果惊人的发现:
包括类名在内的所有命名都发生了重定义行为。
这是否说明C++的类定义实际不是一种类声明行为呢,不尽然。我们可以在namespace.cpp文件中尝试写两条extern int a =10一样的语句,结果同却不会有重定义错误。这样我们可以得出一个初步的猜测:
C++中,类的定义 并不是一种声明行为,而是一种定义行为,所以在同一作用域内是不可以定义多次的。
然后我们只让namespace.cpp包含一个头文件,然后执行下面命令:g++ namespace.cpp car1 car2
报错:car1:file not recognized: File format not recognized
collect2: ld returned 1 exitstatus
说明该命令中的car1 car2无法识别,那么改为.cpp,同时在两个文件的类中都加入一个静态内联函数:
- <span style="font-size:18px;">
- static void view()
- {
- std::cout<<"inline static!"<<std::endl;
- }</span>
然后执行:g++namespace.cpp car1.cpp car2.cpp
此时出现重定义的函数只有:
GetC(),test()----都在类外部实现
test2()-----全局函数
由此可得出本文最开始列出的那句书上的话具有一定的局限性,你可以在不同的文件中定义同一个类,但该类决不可以有类外实现的函数,否则会出现重定义。
推荐:C++中类的定义和实现须放在不同的文件中,类体应该放在头文件中。但外部实现的成员函数应该放在对应的.cpp中,这是最佳实践,避免不必要的错误。
这里似乎有一个困惑的地方,那就是既然类的定义并非声明动作,那么为何在不同文件中定义链接时不会出现重定义呢,而它在外部实现的成员函数却出现重定义呢?
解释:
(1) C++中类的代码区属于文件作用域,类似于全局static数据。
(2) C++中类外实现的函数属于全局作用域,类似全局的非static成员,具有external属性。
(3) 再次强调,类的定义是一种定义行为。
在不同源文件中定义同一个类,但实质上这两个类没有内在的联系,改变其中一个不会影响另外一个,两个类只不过是恰好特征完全一样罢了,就好比牛顿和莱布尼茨尽管都发明了微积分,但它们谁了没抄谁的,谁也不受水的约束。所以这样的设计类的方法是糟糕的,我们并不提倡。
其实上述重定义问题,在C++中是完全有方法避免的,那就是命名空间,如下代码所示:
//car1
- <span style="font-size:18px;">
- #include<iostream>
- namespace nsp{
- class car{
- public:
- car(){std::cout<<"car1:"<<std::endl;}
- car(int v,int c):value(v),capacity(c){std::cout<<"car1:"<<std::endl;}
- void show()
- {
- std::cout<<"show car1:"<<std::endl;
- std::cout<<"value:"<<value<<" capacity:"<<capacity<<std::endl;
- }
- int GetV(){return value;}
- int GetC();
- static void test();
- static void view()
- {
- std::cout<<"inline static!"<<std::endl;
- }
- friend void brother(car &c)
- {
- std::cout<<"inner brother!"<<std::endl;
- }
- friend void sister();
- private:
- int value;
- int capacity;
- };
- void sister()
- {
- std::cout<<"outer sister!"<<std::endl;
- }
- int car::GetC()
- {
- return capacity;
- }
- void car::test()
- {
- std::cout<<"for test!"<<std::endl;
- }
- void test2()
- {
- std::cout<<"outer Func!"<<std::endl;
- }
- }</span>
//car2
- <span style="font-size:18px;">
- #include<iostream>
- namespace liao{
- class car{
- public:
- car(){std::cout<<"car2:"<<std::endl;}
- car(int v,int c):value(v),capacity(c){std::cout<<"car2:"<<std::endl;}
- void show()
- {
- std::cout<<"show car2:"<<std::endl;
- std::cout<<"value:"<<value<<" capacity:"<<capacity<<std::endl;
- }
- int GetV(){return value;}
- int GetC();
- static void test();
- static void view()
- {
- std::cout<<"inline static!"<<std::endl;
- }
- friend void brother(car& c)
- {
- std::cout<<"inner brother!"<<std::endl;
- }
- friend void sister();
- private:
- int value;
- int capacity;
- };
- void sister()
- {
- std::cout<<"outer sister!"<<std::endl;
- }
- int car::GetC()
- {
- return capacity;
- }
- void car::test()
- {
- std::cout<<"for test!"<<std::endl;
- }
- void test2()
- {
- std::cout<<"outer Func!"<<std::endl;
- }
- }</span>
- <span style="font-size:18px;">
- //namespace.cpp
- #include"car1"
- #include"car2"
- using namespace std;
- class A{
- public:
- A(int i):a(i){}
- friend void f(){} //can’t call(无法调用)
- friend void f(A& obj)
- {
- //cout<<obj.a;
- }
- friend ostream& operator<<(ostream& os,A& obj)
- {
- os<<obj.a;
- }
- private:
- int a;
- };
- int main()
- {
- cout<<"***********car1*************"<<endl;
- nsp::car c(100,10);
- c.show();
- std::cout<<c.GetV()<<std::endl;
- std::cout<<c.GetC()<<std::endl;
- nsp::car::test();
- nsp::car::view();
- brother(c);
- //nsp::brother(c);
- nsp::sister();
- nsp::test2();
- cout<<"***********car2*************"<<endl;
- liao::car b(100,10);
- b.show();
- std::cout<<b.GetV()<<std::endl;
- std::cout<<b.GetC()<<std::endl;
- liao::car::test();
- liao::car::view();
- brother(b);
- //liao::brother(b);
- liao::sister();
- liao::test2();
- cout<<"***********test for friend Func*************"<<endl;
- A a(3);
- cout<<a<<endl;
- //f();
- f(a);
- return 0;
- }</span>
输出:
***********car1*************
car1:
show car1:
value:100 capacity:10
100
10
for test!
inline static!
inner brother!
outer sister!
outer Func!
***********car2*************
car2:
show car2:
value:100 capacity:10
100
10
for test!
inline static!
inner brother!
outer sister!
outer Func!
***********test for friend Func*************
3
通过两个命名空间我们就可以区分两个同名的类,把它们的作用域局限在某个空间中,这样就可以防止重定义的行为。道理非常简单不肖多说。
有一个值得说明的发现,这个也与上面得出的结论有关。那就包括友元函数在内的成员函数如果在类内部定义则只有文件作用域,在外部定义的具有全局作用域。此处的全局作用域是指链接属性,访问时还必须加上空间名及作用域操作符。
友元函数在类内部实现有点特殊,如本例中,brother()不直接属于空间nsp和liao而且属于类域构成的空间中,可是友元函数却不是类的一个成员,这就不可以直接用对象调用对象.函数名(),也不能使用类名::函数名()那么怎么调用呢,只能传递一个该类类型的实参,用这个实参来定位函数所在的类域。
但是如果f的形参不是A类型的行不行呢?当然行,但必须具备有效的从A到f形参类型的转换,例如:
- <span style="font-size:18px;">#include <iostream>
- class A
- {
- public:
- friend void f( int a ){ }
- operator int( ){ return a; }
- private:
- int a;
- };
- int main( void )
- {
- A a;
- f( a );
- return 0;
- }</span>
小结:友元函数的调用(包括显示声明为inline的函数)
(1) 若在外部实现,内部声明,链接属性为external。调用形式为空间名::函数名();
(2) 若在内部声明,内部实现,链接属性为internal。
1) 若参数无本类类型,也不能通过隐式类型转换接受本类类型的参数。那么无法调用
2) 反之,则直接用函数名调用(传递正确参数即可)。
关于类与命名空间的问题就到这了,遇到问题再说!
命名空间:命名空间祥解
C++命名空间<转>
熟练掌握C/C++语言,熟悉Windows开发平台,能熟练运用MFC自主编开发出一些应用程序;
熟练掌握SQL语句,对数据库有很好的认识,能熟练使用SQL Server2000软件;
熟练掌握JAVA语言,熟悉J2ME对手机软件开发一定的基础;
深入理解面向对象的思想,并能熟练应用于具体的程序设计开发中;
熟悉Unix/Linux下C语言的编程以及常用的命令,熟悉汇编语言;
熟悉网络的TCP/IP、UDP等协议,能处理解决电脑系统软件常见的故障;
C++ using namespace std 详解
所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
一 :
<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。
后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。
因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespacestd;这样才能正确使用cout。
二:
所谓namespace,是指标识符的各种可见范围。
C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:
std::cout << std::hex<< 3.4<< std::endl;
2、使用using关键字。
using std::cout;
using std::endl;
以上程序可以写成
cout << std::hex<< 3.4<< endl;
3、最方便的就是使用using namespace std;
例如:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:
cout << hex<< 3.4<< endl;
因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。
命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"
using namespace std 的用法
摘自
using namespacestd;用的并不少!
---------------------------------------------------------------
实际上就是告诉编译器,你类型是什么,在哪能找到。
常用的是using namespace std,就是说用C++的标准名字空间。
你也可以引用你自己的名字空间。比如说:
import "C:\\MyTest\\test.tlb"
using namespace CMyTest
就可以引用CMyTest内的各个类型名
看C++ prime
---------------------------------------------------------------
声明该文件使用C++标准库吧!
比如
#include <iostream>
using namespace std;
void main()
{
}
如果不用using namespace std;这句,那么
std::cout << "hello!"<<endl;
这是名字空间的问题!具体参看有关书籍吧,新版的C++ 书应该都有介绍的!
---------------------------------------------------------------
using 指示符!
这是个名字空间问题,是标准C++引入的新概念!
具体在《C++Primer》第8.6节有详细说明!
---------------------------------------------------------------
因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。
---------------------------------------------------------------
名字空间,实质上也是为了方便程序在不同平台上正确的运行。
---------------------------------------------------------------
namespace是为了解决C++中的名字冲突而引入的。
什么是名字冲突呢?比如,在文件x.h中有个类MyClass,
在文件y.h中也有个类MyClass,而在文件z.cpp中要同时
引用x.h和y.h文件。显然,按通常的方法是行不能的,
那怎么办呢?引入namespace即可。例如:
// x.h
namespace MyNamespace1
{
};
// y.h
namespace MyNamespace2
{
};
// z.cpp
#include"x.h"
#include"y.h"
void z::f()
{
}
尽量不要使用using namespace std;VC++2005使用有感
Posted on 2007-11-06 20:28 Samson小天 阅读(1163) 评论(6) 编辑 收藏 网摘 所属分类:C++/C++.net
今天用了VISUAL C++写了个小程序(VS2005),很简单很简单的,但是就是编译不通过
出现一个奇怪的问题:错误 1 error C2668: “max”: 对重载函数的调用不明确
最初代码如下
#include <iostream>
using namespace std;
template <typename T>
T max (T a,T b)
{
return ((a>b)?a:b);
}
void main()
{
double x,y;
cin>>x>>y;
cout<<"Max number is"<<(max(x,y))<<endl;
cin>>x;
}
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T>
T max (T a,T b)
{
return ((a>b)?a:b);
}
int main()
{
double x,y;
cin>>x>>y;
cout<<"Max number is"<<(max(x,y))<<endl;
cin>>x;
}
很多C++程序员还在使用而不是用更新的标准的库。
这两者都有什么不同呢?首先,5年前我们就开始反对把.h符号继续用在标准的头
文件中。继续使用过时的规则可不是个好的方法。从功能性的角度来讲,
<iostream>包含了一系列模板化的I/O类,相反地<iostream.h>只仅仅是支持字符
流。另外,输入输出流的C++标准规范接口在一些微妙的细节上都已改进,因此,
<iostream>和<iostream.h>在接口和执行上都是不同的。最后,<iostream>的各组
成都是以STL的形式声明的,然而<iostream.h>的各组成都是声明成全局型的。
因为这些实质上的不同,你不能在一个程序中混淆使用这两个库。做为一种习
惯,在新的代码中一般使用<iostream>,但如果你处理的是过去编写的代码,为了
继承可以用继续用<iostream.h>旧保持代码的一致性。
///////////////////
<iostream>表示你使用的是标注命名空间,也就是在程序开始应该有这么一句话
using namespace std ;
这是遵循c++标准的
<iostream.h>
则没有遵循c++标准
////////////////
<string.h>是旧的C头文件,对应的是基于char*的字符串处理函数;
<string>是包装了std的C++头文件,对应的是新的strng类;
<cstring>是对应旧的C头文件的std版本。
在C++语言编写的程序中,变量和函数等的作用范围是有一定限制的。比如,在函数体中定义的一个临时变量就不可以在函数体外使用。为了解决变量和函数等的作用范围,在C++语言中引入了名空间的概念,并增加了关键字namespace和using
下面通过例程说明关键字namespace的用法。
#include
#include
namespace
{
}
namespace
{
}
namespace
{
}
namespace
int
void
{