C++ 命名空间 & vector & 模板

命名空间

为了区分不同库中相同名称的函数、类、变量等,引入概念:命名空间。它可作为附加信息来帮助区分它们。使用了命名空间即定义了上下文,本质上就是定义了一个范围。

定义命名空间

命名空间的定义使用关键字 namespace,后跟命名空间的名称,如下所示:

cpp 复制代码
namespace namespace_name {
   // 代码声明
}

为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:

cpp 复制代码
namespace_name::code;    // code 可以是变量或函数

命名空间为变量或函数等实体定义范围的实例:

cpp 复制代码
#include<iostream>
using namespace std;

/* 第一个命名空间 */
namespace First_space{
	void func()
	{
		cout<<"Inside first_space"<<endl;
	}
}

/* 第二个命名空间 */
namespace Second_space{
	void func()
	{
		cout<<"Inside second_space"<<endl;
	}
}

int main()
{
	First_space::func();    // 调用第一个命名空间中的函数
	
	Second_space::func();   // 调用第二个命名空间中的函数
	
	return 0;
}

执行结果如下:

Inside first_space

Inside second_space

using 指令

在使用命名空间时,用 using namespace 指令,就可以不用在函数或变量前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

cpp 复制代码
using namespace First_space;

using 指令也可以用来指定命名空间中的特定项目,在随后的代码中,就可以不用加上命名空间名称作为前缀了。但是 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀。例如,只打算使用命名空间 std 中的 cout 部分,如下所示:

cpp 复制代码
#include <iostream>
using std::cout;     // 指定命名空间中的的 cout 
 
int main ()
{
   /* 没有指定 std 中的 endl, 仍然需要加上命名空间名称作为前缀 */
   cout << "std::endl is used with std!" << std::endl;
   
   return 0;
}

using 指令引入的名称遵循正常的范围规则。名称从使用 using 指令开始是可见的,直到该范围结束。此时,在范围以外定义的同名实体是隐藏的。

不连续的命名空间

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:

cpp 复制代码
namespace namespace_Name {
   // 代码声明
}

嵌套的命名空间

命名空间可以嵌套,即,在一个命名空间中定义另一个命名空间,如下所示:

cpp 复制代码
namespace namespace_Name1 {
   // 代码声明
   namespace namespace_Name2 {
      // 代码声明
   }
}

并通过使用 :: 运算符来访问嵌套的命名空间中的成员:

cpp 复制代码
/* 访问 namespace_name1 中的成员 */
using namespace namespace_Name1;

/* 访问 namespace_name2 中的成员 */ 
using namespace namespace_Name1::namespace_Name2;
 

注意:使用 namespace_Name1,那么在该范围内的 namespace_Name2 中的元素也是可用的,如下所示:

cpp 复制代码
#include <iostream>
using namespace std;

namespace A
{
    int a = 100;
    namespace B      //嵌套一个命名空间B
    {
        int a =20;
    }
}

int a = 200;         //定义一个全局变量

int main()
{
    cout <<"A::a ="<< A::a << endl;
    cout <<"A::B::a ="<< A::B::a << endl;
    cout <<"a ="<< a << endl;         // 全局变量 a   
    cout <<"::a ="<< ::a << endl;     // 全局变量 a 

    /* 添加一个同名局部变量,注意局部变量和全局变量的区分 */
    int a = 30;                       // 局部变量 a
    cout <<"局部变量 a ="<< a << endl;
    cout <<"::a ="<< ::a << endl;     // 全局变量 a 

    return 0;
}

全局变量 a 表达为 ::a,用于当有同名的局部变量时来区别两者。执行结果如下:

A::a =100

A::B::a =20

a =200

::a =200

局部变量 a =30

::a =200

容器(vector )

定义

Vector是最常用的容器之一,是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组,因为其大小是根据实时更新而变化的。

vector的定义如下:

cpp 复制代码
Vector<类型> 标识符
Vector<类型> 标识符(最大容量)
Vector<类型> 标识符(最大容量,初始所有值)
Vector<类型> v(i,i+2);     //得到i索引值为3以后的值
Vector< vector< int> >v;   //二维向量,这里最外的<>要有空格。否则在比较旧的编译器下无法通过

例如:

cpp 复制代码
vector<int> v;           // 储存int型的值 
vector<double> v;        // 储存double型的值 
vector<string> v;        // 储存string型的值 
vector<struct> v;        // 储存结构体或者类的值的值 

/* 可以定义vector数组 */
vector<int> a[n];        // 储存int型的值 
vector<double> a[n];     // 储存double型的值 

特性

容器特性:

  • 顺序序列:顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。
  • 动态数组:支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。提供了在序列末尾相对快速地添加/删除元素的操作。
  • 内存分配感知(Allocator-aware):容器使用一个内存分配器对象来动态地处理它的存储需求。

成员函数

构造函数

cpp 复制代码
vector();                       //创建一个空vector
vector(int nSize);              //创建一个vector,元素个数为nSize
vector(int nSize,const t& t);   //创建一个vector,元素个数为nSize,且值均为t
vector(const vector&);          //复制构造函数
vector(begin,end);              //复制[begin,end)区间内另一个数组的元素到vector中

增加函数

cpp 复制代码
/* 向量尾部增加一个元素X */
void push_back(const T& x);    
/* 向量中迭代器指向元素前增加一个元素x */              
iterator insert(iterator it,const T& x); 
/* 向量中迭代器指向元素前增加n个相同的元素x */    
iterator insert(iterator it,int n,const T& x); 
/* 向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据 */
iterator insert(iterator it,const_iterator first,const_iterator last);

删除函数

cpp 复制代码
iterator erase(iterator it);                   //删除向量中迭代器指向元素
iterator erase(iterator first,iterator last);  //删除向量中[first,last)中元素
void pop_back();                               //删除向量中最后一个元素
void clear();                                  //清空向量中所有元素

遍历函数

cpp 复制代码
reference at(int pos);       //返回pos位置元素的引用
reference front();           //返回首元素的引用
reference back();            //返回尾元素的引用
iterator begin();            //返回向量头指针,指向第一个元素
iterator end();              //返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin();   //反向迭代器,指向最后一个元素
reverse_iterator rend();     //反向迭代器,指向第一个元素之前的位置

判断函数

cpp 复制代码
bool empty() const;          //判断向量是否为空,若为空,则向量中无元素

大小函数

cpp 复制代码
int size() const;             //返回向量中元素的个数
int capacity() const;         //返回当前向量所能容纳的最大元素值
int max_size() const;         //返回最大可允许的vector元素数量值

/* 如果n小于当前容器的大小,则将内容减少到其前n个元素,并删除超出范围的元素(并销毁它们)*/
void resize(size_type n);    

/**
  *如果n大于当前容器的大小,则通过在末尾插入所需数量的元素来扩展内容,以达到n的大小。
  *如果指定了val,则将新元素初始化为val的副本,否则将对它们进行值初始化。
  *如果n也大于当前容器容量,将自动重新分配已分配的存储空间 
  */
void resize(size_type n, const value_type& val);

/**
  *表示预分配n个元素的存储空间,但不是真正的创建对象,
  *需要通过insert()或push_back()等操作创建对象。
  *调用reserve(n)后,若容器的capacity<n,则重新分配内存空间,从而使得capacity等于n。
  *若capacity>=n,capacity无变化。
  */
void reserve(size_type n);

注意:容器调用resize()函数后,所有的空间都已经初始化了,所以可以直接访问。而reserve()函数预分配出的空间没有被初始化,所以不可访问。resize和reverse区别(capacity:容器可存储的最大总数,size:当前容器存储的个数):

  • reserve()只修改capacity大小,不修改size大小,
  • resize()既修改capacity大小,也修改size大小。

其他

cpp 复制代码
/* 交换两个同类型向量的数据 */
void swap(vector&);
/* 设置向量中前n个元素的值为x */
void assign(int n,const T& x);//
/* 向量中[first,last)中元素设置成当前向量元素 */
void assign(const_iterator first,const_iterator last);

实例

pop_back() & push_back()

在容器最后移除和插入数据:

cpp 复制代码
#include<iostream>
#include<vector>
#include<string.h>

using namespace std;

int main()
{
	vector<int> obj;               // 创建一个向量存储容器 int
	for(int i=0;i<10;i++)          // 依次在数组最后添加数据
	{
		obj.push_back(i);
		cout<<obj[i]<<"\t";
	}
	
	for(int j=0;j<5;j++)           // 依次去掉数组最后一个数据 
	{
		obj.pop_back();
	}
	
	cout<<"\n"<<endl;
	
	for(int k=0;k<obj.size();k++)  // size()返回容器中实际数据个数 
	{
		cout<<obj[k]<<"\t";
	}
	
	return 0;
}

执行结果如下:

0 1 2 3 4 5 6 7 8 9

0 1 2 3 4

clear()

清除容器中所有数据:

cpp 复制代码
#include<string.h>
#include<vector>
#include<iostream>

using namespace std;

int main()
{
	vector<int> obj;
	for(int i=0;i<10;i++)
	{
		obj.push_back(i);
		cout<<obj[i]<<"\t";
	}
	
	obj.clear();
	cout<<endl;
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	cout<<endl<<"size = "<<obj.size()<<", capacity = "<<obj.capacity();
	
	return 0;
}

执行结果如下:

0 1 2 3 4 5 6 7 8 9

size = 0, capacity = 16

排序

sort() 函数需要包含头文件: #include <algorithm>

cpp 复制代码
#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>    // 'sort()' and 'reverse()' was declared in this Header file

using namespace std;

int main()
{
	vector<int> obj;
	
	obj.push_back(1);
	obj.push_back(2);
	obj.push_back(3);
	
	cout<<"由小到大:"<<endl;
	sort(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	cout<< endl <<"由大到小:"<<endl;
	reverse(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
    return 0;	
}

执行结果如下:

由小到大:

1 2 3

由大到小:

3 2 1

如果想 sort 来降序,可重写 sort

cpp 复制代码
/* 升序排列,如果改为return a>b,则为降序  */
bool compare(int a,int b) 
{ 
    return a< b; 
} 

/* 示例 */
int a[20]={2,4,1,23,5,76,0,43,24,65},i; 
for(i=0;i<20;i++) 
{
    cout<< a[i]<< endl; 
}
sort(a,a+20,compare);

如下所示:

cpp 复制代码
#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>    // 'sort' and 'reverse' was declared in this Header file

using namespace std;

bool compare(int a,int b) 
{ 
    return a > b; 
} 

int main()
{
	vector<int> obj;
	
	obj.push_back(1);
	obj.push_back(2);
	obj.push_back(3);
	
	cout<<"由小到大:"<<endl;
	sort(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	cout<< endl <<"由大到小:"<<endl;
	sort(obj.begin(),obj.end(),compare);  //sort 来降序
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
    return 0;	
}

直接数组访问&迭代器访问

cpp 复制代码
#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
	vector<int> obj;
	for(int i=0;i<10;i++)
	{
		obj.push_back(i);
	}

	// 方法一:直接数组访问
	cout<<"直接利用数组:"<<"\t";
	for(int i=0;i<10;i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	// 方法二:迭代器访问
	cout<<endl;
	cout<<"利用迭代器:"<<"\t";
	vector<int>::iterator it;
	for(it=obj.begin();it!=obj.end();it++)
	{
		cout<<*it<<"\t";
	}
	
	return 0;
}

执行结果如下:

直接利用函数: 0 1 2 3 4 5 6 7 8 9

利用迭代器: 0 1 2 3 4 5 6 7 8 9

二维数组两种定义方法

假设定义一个二维数组obj[5][6],两种定义方法的结果一样:

方法一

cpp 复制代码
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>

using namespace std;

int main()
{
	int N=5,M=6;                   // 定义二维动态数组大小5行为5行6列,值默认全为0
	vector< vector<int> > obj(N);  // 开辟行
	for(int i=0;i<obj.size();i++)
	{
		obj[i].resize(M);          // 开辟行
	}
	
	for(int i=0;i<obj.size();i++)  // 赋值,并输出二维动态数组 
	{
		for(int j=0;j<obj[i].size();j++)
		{
			obj[i][j]=i+j;
			cout<<obj[i][j]<<"\t"; 
		}
		cout<<endl;
	}
	
	return 0;
}

方法二

cpp 复制代码
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>

using namespace std;

int main()
{
	int N=5,M=6;                   // 定义二维动态数组大小5行为5行6列,值默认全为0
	
	/* 创建一个vector,元素个数为N,且值均为一个元素个数为M的vector<int>(M) */ 
	vector< vector<int> > obj(N,vector<int>(M)); 
	
	for(int i=0;i<obj.size();i++)  // 赋值,并输出二维动态数组 
	{
		for(int j=0;j<obj[i].size();j++)
		{
			obj[i][j]=i+j;
			cout<<obj[i][j]<<"\t"; 			
		}
		cout<<endl;
	}

	return 0;
}

输出结果为:

0 1 2 3 4 5

1 2 3 4 5 6

2 3 4 5 6 7

3 4 5 6 7 8

4 5 6 7 8 9

模板

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量(vector) ,我们可以定义许多不同类型的向量,比如 vector <int>vector <string>

可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。

函数模板

模板函数定义的一般形式如下所示:

cpp 复制代码
template <typename Type> 
return_Type func_Name(parameter list)
{
   // 函数的主体
}

在这里,Type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。

函数模板可以重载,只要它们的形参表不同即可。例如,下面两个模板可以同时存在:

cpp 复制代码
template <class T1, class T2>
void print(T1 arg1, T2 arg2)
{
  cout << arg1 <<" "<< arg2 << endl; 
}

template <class T>
void print(T arg1, T arg2)
{
  cout << arg1 <<" "<< arg2 << endl;
}

下面是函数模板的实例,返回两个数中的最大值:

cpp 复制代码
#include <iostream>
#include <string>

using namespace std;

template <typename T>
inline T const& Max(T const& a,T const& b)
{
	return a < b ? b:a;
}

int main()
{
   int i = 30;	
   int j = 20;	
   cout<<"Max(i,j): "<<Max(i,j)<<endl;
   
   string s1 = "hello";
   string s2 = "world";
   cout<<"Max(si,s2): "<<Max(s1,s2)<<endl;
   
   return 0;
}

执行结果如下:

Max(i,j): 30

Max(si,s2): world

类模板

类模板泛型类声明的一般形式如下所示:

cpp 复制代码
template <class type> 
class class_Name
{
    ...
}

type 是占位符类型名称,可以在类被实例化的时候进行指定。使用一个逗号分隔的列表可以定义多个泛型数据类型。

在模板定义语法中关键字 class 与 typename 的作用完全一样,所以类模板泛型类声明也可以是这样:

cpp 复制代码
template <typename type> 
class class_Name
{
    ...
}

下面的实例定义了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操作:

cpp 复制代码
#include<iostream>
#include<vector>
#include<cstdlib>
#include<string>
#include<stdexcept>

using namespace std;

template <class T>
class Stack
{
private:
    vector<T> elements;
public:
    void Push(T const&);
	void Pop();
	T Top() const;
	bool empty() const
	{
		return elements.empty();
	}
};

template <class T>
void Stack<T>::Push(T const& elem)
{
	elements.push_back(elem);
}

template <class T>
void Stack<T>::Pop () 
{ 
    if (elements.empty()) { 
        throw out_of_range("Stack<>::pop(): empty stack"); 
    }
    // 删除最后一个元素
    elements.pop_back();         
} 

template <class T>
T Stack<T>::Top() const
{
    if(elements.empty())
	{
        throw out_of_range("Stack<T>::Top(): empty stack");      
	}		
	return elements.back();
}

int main()
{
	try{
		Stack<int> intStack;
		Stack<string> stringStack;
		
		intStack.Push(7);
		cout<<intStack.Top()<<endl;
		
		stringStack.Push("hello");
		cout<<stringStack.Top()<<std::endl;
		stringStack.Pop();
		stringStack.Pop();
	} catch(exception const& ex) {
		cerr<<"exception: "<<ex.what()<<endl;
		return -1;
	}
	
    return 0;
}

执行结果如下:

7

hello

exception: Stack<>::pop(): empty stack

相关推荐
霁月风10 分钟前
设计模式——适配器模式
c++·适配器模式
萧鼎21 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸22 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农22 分钟前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^23 分钟前
数据库连接池的创建
java·开发语言·数据库
秋の花31 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
jrrz082832 分钟前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
小松学前端34 分钟前
第六章 7.0 LinkList
java·开发语言·网络
可峰科技43 分钟前
斗破QT编程入门系列之二:认识Qt:编写一个HelloWorld程序(四星斗师)
开发语言·qt
咖啡里的茶i1 小时前
Vehicle友元Date多态Sedan和Truck
c++