C++中的using 的作用,typedef与#define的区别

发布时间 2023-06-01 15:48:59作者: 阿风小子

我们用到的库函数基本上都属于命名空间std的,在程序使用的过程中要显示的将这一点标示出来,如std::cout。这个方法比较烦琐,而我们都知道使用using声明则更方便更安全。

2、命令空间的using声明

我们在书写模块功能时,为了防止命名冲突会对模块取命名空间,这样子在使用时就需要指定是哪个命名空间,使用using声明,则后面使用就无须前缀了。例如:

using std::cin;	//using声明,当我们使用cin时,从命名空间std中获取它
int main()
{
	int i;
	cin >> i;	//正确:cin和std::cin含义相同
	cout << i;	//错误:没有对应的using声明,必须使用完整的名字
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

需要注意的是每个名字需要独立的using声明。例如:

using std::cin;	//必须每一个都有独立的using声明
using std::cout;  using std::endl;	//写在同一行也需要独立声明
  • 1
  • 2

位于头文件的代码一般来说不应该使用using声明。因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明,有可能产生名字冲突。

3、在子类中引用基类成员

在子类中对基类成员进行声明,可恢复基类的防控级别。有三点规则:

  1. 在基类中的private成员,不能在派生类中任何地方用using声明。
  2. 在基类中的protected成员,可以在派生类中任何地方用using声明。当在public下声明时,在类定义体外部,可以用派生类对象访问该成员,但不能用基类对象访问该成员;当在protected下声明时,该成员可以被继续派生下去;当在private下声明时,对派生类定义体外部来说,该成员是派生类的私有成员。
  3. 在基类中的public成员,可以在派生类中任何地方用using声明。具体声明后的效果同基类中的protected成员。

例如:

class Base 
{
protected:
    void test1() { cout << "test1" << endl; }
    void test1(int a) {cout << "test2" << endl; }

    int value = 55;
};
 
class Derived : Base 	//使用默认继承
{
public:
    //using Base::test1;	//using只是声明,不参与形参的指定
    //using Base::value;
    void test2() { cout << "value is " << value << endl; }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

我们知道class的默认继承是private,这样子类中是无法访问基类成员的,即test2会编译出错。但是如果我们把上面注释的声明给放开,则没有问题。

注意:using::test1只是声明,不需要形参指定,所以test1的两个重载版本在子类中都可使用。

4、使用using起别名

相当于传统的typedef起别名。

typedef 	std::vector<int> intvec;
using 	intvec	= std::vector<int>;	//这两个写法是等价的
  • 1
  • 2

这个还不是很明显的优势,在来看一个列子:

typedef void (*FP) (int, const std::string&);
  • 1

若不是特别熟悉函数指针与typedef,第一眼还是很难指出FP其实是一个别名,代表着的是一个函数指针,而指向的这个函数返回类型是void,接受参数是int, const std::string&。

using FP = void (*) (int, const std::string&);
  • 1

这样就很明显了,一看FP就是一个别名。using的写法把别名的名字强制分离到了左边,而把别名指向的放在了右边,比较清晰,可读性比较好。比如:

typedef std::string (Foo::* fooMemFnPtr) (const std::string&);
    
using fooMemFnPtr = std::string (Foo::*) (const std::string&);
  • 1
  • 2
  • 3

来看一下模板别名。

template <typename T>
using Vec = MyVector<T, MyAlloc<T>>;
 
// usage
Vec<int> vec;
  • 1
  • 2
  • 3
  • 4
  • 5

若使用typedef

template <typename T>
typedef MyVector<T, MyAlloc<T>> Vec;
 
// usage
Vec<int> vec;
  • 1
  • 2
  • 3
  • 4
  • 5

当进行编译的时候,编译器会给出error: a typedef cannot be a template的错误信息。

那么,如果我们想要用typedef做到这一点,需要进行包装一层,如:

template <typename T>
struct Vec
{
  typedef MyVector<T, MyAlloc<T>> type;
};

// usage
Vec<int>::type vec;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

正如你所看到的,这样是非常不漂亮的。而更糟糕的是,如果你想要把这样的类型用在模板类或者进行参数传递的时候,你需要使用typename强制指定这样的成员为类型,而不是说这样的::type是一个静态成员亦或者其它情况可以满足这样的语法,如:

template <typename T>
class Widget
{
  typename Vec<T>::type vec;
};
  • 1
  • 2
  • 3
  • 4
  • 5

然而,如果是使用using语法的模板别名,你则完全避免了因为::type引起的问题,也就完全不需要typename来指定了。

template <typename T>
class Widget
{
  Vec<T> vec;
};
  • 1
  • 2
  • 3
  • 4
  • 5

一切都会非常的自然,所以于此,模板起别名时推荐using,而非typedef。

所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。

     iostream和iostream.h的区别:

     后缀为.h的头文件C++标注已经明确提出不支持了。早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,C++标准为了和C区别开,也为了正确地使用命名空间,规定头文件不使用后缀.h。因此,当使用<iostream.h>时,相当于在C中调用库函数,使用的是全局命名空间,也就是早期的C++实现。当使用<iostream>时,该头文件没有定义全局命名空间,必须使用namespace std,这样才能使用类似于cout这样的C++标识符。

namespace是指标识符的各种可见范围。
C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。 由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
直接指定标识符
例如std::iostream而不是iostream。完整语句如下: std::cout << std::hex << 3.4 << std::endl;
使用using关键字
using std::cout; using std::endl; using std::cin; 以上程序可以写成如下代码:
using std::cout <<using std::hex << 3.4 <<using std:: endl;
使用using namespace std
例如:
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写: cout << hex << 3.4 << endl; 因为标准库非常的庞大,所以程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都放在名字空间std中。但这有会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的,所以就有了<iostream>和<iostream.h>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般都不加".h"。