C++使用基础


前言

一、标识符作用域

函数原型作用域:函数原型声明时的形参
局部作用域:函数形参,函数体内声明的变量(从声明处到所在块结束的大括号)
类作用域:类的成员
命名空间作用域: 具有的变量称全局变量

命名空间内部可直接使用其中标识符: using namespace 命名空间名;(不宜放在头文件中)
namespace 命名空间名{
命名空间内的各种声明(函数声明、类声明、。。。。)  }
匿名命名空间:不会暴露给其他编译单元
		namespace {
命名空间内的各种声明(函数声明、类声明、。。。。)  }

引用其他命名空间标识符或指定命名空间中的特定项目:

using  命名空间名::标识符名;
命名空间名::标识符名;
赋值  NS::j=6;

允许嵌套: 命名空间名1::命名空间名2::标识符名; //如果使用命名空间1,在该范围 内命名空间2中的元素也是可用的。

::作用域分辨符

不同作用域声明的标识符:外层声明,内层未再次声明,外层标识符依然可见;内层声明同名标识符,则外层标识符在内层不可见

当有函数外全局变量a,函数内局部变量a时,全局变量a表达为 :a 来区分两者,派生类在内层,基类包含内层

若派生类中定义了与基类同名成员函数,即使函数参数表不同,从基类继承的同名函数的所有重载形式也会被隐藏。若要访问,就要使用作用域分辨符和基类名限定

若派生类的多个基类有同名成员,同时派生类又新增同名成员,将隐藏所有基类同名成员。
唯一标识和访问派生类新增成员:对象名 ->成员名 、 对象名.成员名
作用域分辨符和基类名标识基类成员:对象名.基类名::成员名 、 对象名 ->基类名::成员名
若派生类又不新增同名成员,则不可用对象名.成员名,可用using 类名::成员名 。且此方法用于基类中函数名,若派生类中定义同名但参数不同的函数,基类函数不会隐藏。

Door::DoorOpenDirection OpenDirection::Direction() const
{
    return m_direction;
}

DoorOpenDirection是Door类中的公共成员枚举,Door::DoorOpenDirection整体是OpenDirection类Direction函数的返回类型

可见性

同一作用域不能声明同名标识符
有包含关系的作用域声明同名标识符,外层的在内层不可见
无包含关系的,互不影响

二、内联函数inline

在编译时将函数本体嵌入在调用处,节省参数传递、控制转移开销
定义时前加关键字inline,调用处直接用;

三、引用传递&

int i;
int &ri = i;建立一个int型的引用ri,初始化为i的一个别名
声明一个引用时必须同时初始化指向一个已存在对象,且不能改为指向其他对象
可通过改变ri的值改变i的值

四、静态成员

解决同一个类不同对象之间数据、函数共享问题
数据成员:静态数据成员具有静态生存期,不属于任何对象,可通过类名访问(生存期在文章《C++基础运算与数据结构》中)

类名::标识符
必须在类定义之外即命名空间作用域使用类名限定定义性声明
	class Point{
	.....
	private:
		static int count;
	};
int Point::count =0;

函数成员:静态成员函数可通过类名和对象名调用,非静态成员函数只能通过对象名
静态成员函数可直接访问该类的静态数据和函数成员,访问非静态成员须通过对象名

class A {
public:
	static void fun(A a);
private:
	int x;};

void A::fun(A a) {
	cout << a.x;  
	cout << x;  }   //对x的引用错误

五、类型转换

cout<<Line(Point(1),Point(4)).GetLen(); 整型数据转换成Point型对象的显式类型转换
所以等效于cout<<Line((Point)1,(Point)4).GetLen();
cout<<Line(static_cast(1),static_castPoint(4)).GetLen();
隐式:cout<<Line(1,4).GetLen();

只允许显式转换方式:构造函数前加explicit
explicit Point(){…}
若声明与实现分离,关键字应写在类定义中的函数原型声明处,不能在类定义外

1、const_cast

用于强制去掉不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。

2、static_cast

static_cast用于将一种数据类型强制转换为另一种数据类型。

3、reinterpret_cast

主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。

4、dynamic_cast

执行基类向派生类的转换(将一个基类对象指针(或引用)转换到继承类指针)
将基类的指针或引用显式转换成派生类的,转换前会检查所指向对象的实际类型是否与转换的目的类型兼容,不兼容会得到空指针或抛出异常
转换前类型必须是指向多态类型的指针或引用,

特殊用法:判断当前数据类型

Base * d= dynamic_cast<Base > (b);
Entity是Board父类的父类的父类···
Entity
pChild = pComponent->ChildAt(i);
Board* pBoard = dynamic_cast<Board*>(pChild);
if (pBoard != nullptr) //遍历Component的所有孩子及以下时,这一个强制转换如果此孩子是Board类型,则pBoard 不是空指针,否则为空指针
此方法可用来判断此时孩子节点的数据类型

六、STL标准模板库

1、迭代器概念

正向迭代器
容器类名::iterator 迭代器名;

常量正向迭代器
容器类名::const_iterator 迭代器名;

反向迭代器
容器类名::reverse_iterator 迭代器名;

常量反向迭代器
容器类名::const_reverse_iterator 迭代器名;

对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素;
而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。

  vector<int>::iterator i;  //定义正向迭代器
  for (i = v.begin(); i != v.end(); ++i) { } //用迭代器遍历容器

begin 成员函数返回指向容器中第一个元素的迭代器。++i 使得 i 指向容器中的下一个元素。end 成员函数返回的不是指向最后一个元素的迭代器,而是指向最后一个元素后面的位置的迭代器,因此循环的终止条件是i != v.end()。

2、容器

S 为类 m为S的实例

所有容器基本功能函数

m.begin()  //返回指向m中第一个元素的迭代器,数据类型是iterator
m.end()  //返回指向m中最后一个元素的下一个位置的迭代器,数据类型是iterator
m.rbegin(); //得到指向m的最后一个元素的逆向迭代器
m.rend(); //得到指向m的第一个元素的前一个位置的逆向迭代器
m.clear(); //清空容器m的内容,
m.size(); //返回m元素个数
m.empty(); //返回m是否为空,返回值为bool
m.swap(m2); //将m与m2的内容交换。先用m2的内容创建一个临时容器对象
m[n] 等价于 m.begin()[n]  //获得容器的第n个元素

顺序容器:vector、list、deque

初始化

S m(n, t); 或 m.assign(n, t);  //n个t元素组成s
S m(n); 或 m.assign(n); //有n个元素的s
S m(p, q); 或  m.assign(p, q); //[p, q) 区间内的数据作为s的元素

元素插入

m.insert(p , t); //p指向的位置插入t,在原p和p-1指向元素之间,返回指向新插入元素的迭代器
m.insert(p , n, t); //p指向的位置插入n个新元素t,在原p和p-1指向元素之间,无返回值
m.insert(p , q1, q2); //[q1, q2) 区间内的元素顺序插入p位置,在原p和p-1指向元素之间
m.push_back();  //在末尾插入一个元素
m.push_front();  //在头部插入一个元素

元素删除

m.erase( p ); //删除p指向元素,返回被删除元素下一个元素的迭代器,可以不接收返回的迭代器(vector仅仅能删除内容,不改变容量大小即不释
              //放内存)
m.erase( p, q); //删除[p, q) 区间内的元素,返回最后一个被删除元素的下一个元素的迭代器,即删除前q所指元素
                 //迭代器
m.pop_back();   //删除最后一个元素
m.pop_front();   //删除第一个元素
改变大小
m.resize(n); //改变大小为n,多余元素会被删除,少的会在末尾填充(改变容量和元素个数)

访问

m.front();   //获得容器首元素的引用
m.back();   //获得容器尾元素的引用
①vector
#include <vector>

被封装的动态数组,可具有各种类型(插入新元素,插入位置后的元素顺序后移)
vector<元素类型>数组对象名(数组长度,元素初值);
数组长度是一个表达式,可包含变量。初值可指定,但只能为所有元素指定相同初值

    int x=0;
vector<int>arr(x); 
vector<int> a(b); //用b向量来创建a向量,整体复制性赋值
vector<int> a(b.begin(),b.begin+3); //定义了a值为b中第0个到第2个(共3个)元素
int b[7]={1,2,3,4,5,9,8}; vector<int> a(b,b+7); //从数组中获得初值
注意:下标操作不添加元素
vector<int> vec; 
for (vector<int>::size_type i = 0; i != 10; ++i)
     vec[i] = i;  // vec.push_back(i); 才是对的
试图在vec中插入元素值依次为09的整数。但这里vec是空的vector对象,而且下标只能用于获取已存在的元素。
试图对不存在的元素进行下标操作是程序设计过程中经常会犯的严重错误。缓冲区溢出”错误就是对不存在的元素进行下标
操作的结果。
vector<int> ivec;      // 为空
cout << ivec[0]; 

m.capacity(n);   //返回m的容量,能存储的元素总数。
m.reserve(n);   //若当前容量小于n,扩大s的容量使其不小于n。表示容器预留空间,但不是真正的创建对象,需要通过insert()或push_back()等操作创建对象(改变容量,不改变元素个数)
m.clear(); //清空容器m的内容,不改变容量大小即不释放内存
m.erase( p ); //仅能删除内容,不改变容量大小即不释放内存,size改变capacity不变
②list
#include<list>

list < 数据类型 > 链表名 = { 数据1, 数据2, ··· };
对数组对象元素访问: 数组对象名[下标表达式]
合并

m.splice(p, s);  //将s所有元素插入m的p-1和p之间,将s清空
m.splice(p, s, q);  //将s中q所指元素插入m的p-1和p之间,将q所指元素从s删除
m.splice(p, s, q1, q2);  //将s中[q1, q2) 区间所有元素插入m的p-1和p之间,将[q1, q2) 区间元素从s删除
remove(const T & val)	删除和 val 相等的元素
remove_if() 删除指定条件元素
sort() 将链表从小到大排序
reverse() 把list的元素倒转
unique()	删除所有和前一个元素相等的元素
merge(list <T> & x)	将链表 x 合并进来并清空 x。要求链表自身和 x 都是有序的
get_allocator() 返回list的配置器
max_size() 返回list能容纳的最大元素数量

顺序容器适配器:stack、queue

m.size();  //返回元素个数
m.empty();  //返回是否为空
m.push( t );  //压入元素t到m中
m.pop();   //从m弹出一个元素

栈stack

#include<stack>
stack <数据类型>stk;
m.top();  //返回栈顶元素的引用

队列queue

m.front();  //获得队头元素的引用
m.back();   //获得队尾元素的引用

关联容器:set、map(单重),multiset、multimap(多重)

二元组pair

#include <utility>

例map<int, int>的元素类型是pair<int, int>
关联容器的键之间必须能够使用“<”比较大小;<操作符重载提供operator<比较操作,如果没有那么 map模板必须增加第三个模板参数,直接自定义比较函数的函数对象。
S m(p, q); //[p, q) 区间内的数据作为s的元素
单重:数据中出现相同建的元素时,只加入第一个元素
多重;所有元素无条件加入
元素插入

m.insert(t); //插入t,
单重:存在不同键的元素时才能插入,返回类型为pair<S::iterator, bool>,插入成功返回被插入元素的迭代器、true,
否则返回与t的键相同的元素的迭代器
多重:插入会成功,返回已插入元素的迭代器
m.insert(p, q); //按顺序对[p, q) 区间内的每个元素顺序分别执行m.insert(t);

元素删除

m.erase( p ); //删除p指向元素
m.erase( p, q); //删除[p, q) 区间内的元素
m.erase( k ); //删除所有键为k的元素,返回被删除元素的个数

查找

m.find(k); //找到任意一个键为k的元素,返回该元素迭代器。如果没有要此元素,返回m.end()
m.count(k); //返回键为k的元素个数
m.lower_bound(k); //返回要查找关键字的下界的迭代器,m中第一个键值不小于k的
m.upper_bound(k); //返回要查找关键字的上界的迭代器。m中第一个键值大于k的
m.equal_range(k); //返回一个pair,pair里面第一个变量是lower_bound返回的迭代器,pair里面第二个迭代器是upper_
         bound返回的迭代器。该区间包含所有键为k的元素,如果这两个迭代器相等的话,则说明map中不出现这个关键字
1234的话,lower_bound(2)返回2,而upper-bound(2)返回是3
①map
#include <map>

运用了哈希表地址映射key-value的思想实现,内部实现了一个红黑树,map中的元素是自动按Key升序排序

map<数据类型, 数据类型> Map =
		{
			{···, ··}, {···,··}, {···, ··}
		};
迭代器->first  first表示的是key,second存的是value
map<string, int> m; //<>里的第一个参数表示key的类型,第二个参数表示value的类型 

插入元素方式

m.insert(pair<string, int>("Harris", 89)); //通过insert函数插入pair数据
m.insert(map<string, int>::value_type("Harris", 89)); //通过insert函数插入value_type数据
m["Kobe"] = 100;   //数组方式插入(此方式可覆盖以前该关键字对应的值,前两种在关键字已存在时无法插入数据)
string s("Jordan");     m[s] = 90;   //增加元素
m.erase("Kobe");//通过关键字来删除
注意:
if(i == Map["123"]); //存在隐患,如果map包含key没有问题,如果map不包含key,使用下标有一个危险的副作用,会在map中插入一个key的元素,value取默认值,返回value。也就是说,map[key]不可能返回null。
map<string, string>::iterator iter = Map.find("123");
if (iter != Map.end() &&(i == iter->second) ){ }  //防止越界,按序查找未找到时迭代器指向end

遍历

map<string, string>::reverse_iterator iter;
for(iter = Map.rbegin(); iter != Map.rend(); iter++)

unordered_map
#include <unordered_map>
内部实现了哈希表,因此查找速度非常快,但哈希表的建立比较耗费时间
执行效率要比map高很多,对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的
与map底层实现不同,就外部使用来说一致。

②set
#include <set>

与vector的唯一区别是set里面的元素是有序的且唯一的,添加元素会自动排序
添加的元素set里面本来就存在,那么这次添加操作就不执行。要想用set先加个头文件set。

string

#include <string>

字符串常量应该赋值给指向常量的指针

const char * S="hieiih."; 可直接输出   cout<<S;
创建char数组   char str[3] ={'p''r','\0'};
  	  char str[3] ="pr";
string();    //默认构造函数,建立一个长度为0的串
string(const string& rhs);    //复制构造函数
string(const char *s);   //用指针s指向的字符串常量初始化string类的对象 
string(int n, char c);    //将c中字符重复n次,初始化string类的对象 
string(const string& rhs, unsigned int pos, unsigned int n);    //将对象rhs中的串从位置pos开始取n个字符,初始化string类的对象 
string(const char *s, unsigned int n);    //用指针s所指向字符串中前n个字符初始化
getline(cin, 标识符);
string s1;//初始化字符串,空字符串
string s3 = "How am I"; //直接初始化,s3存了字符串

string s2 = s1; //拷贝初始化,深拷贝字符串
string s5(s3); //拷贝初始化,深拷贝字符串
string s7 = string(6, 'c'); //拷贝初始化,cccccc

string s8 = s3 + s7;//将两个字符串合并成一个
s3 = s7;//用一个字符串来替代另一个字符串的对用元素

成员函数

size();//字符串大小
substr(pos, n);//截取字符串中从pos开始的n个字符的子串
count(first, last, value);  //在序列中统计某个值出现的次数。first是容器的首迭代器,last是容器的末迭代器,value是询问的元素。
count_if(first, last, value, cmp); //在序列中统计与某谓词匹配的次数。cmp为比较函数(cmp返回值为布尔型)。

遍历:定义一个遍历字符c,让它分别等于字符串s里面的各个字符,然后执行下面的语句,当c被赋值为s里面所有字符各一次后,就会退出这个循环.
①for (char& c : s)直接引用原字符串进行遍历操作
②for (char c : s)复制一个s字符串再进行遍历操作,复制一个字符串花费大量时间

3、其他使用

①修整过剩容量

(摘自Effective STL 条款17)
vector:

class S {      };
vector<S> m;
vector<S>(m).swap(m);   //使容量和这个实现可以尽量给容器的当前大小一样小size=capacity,并不是使容量尽可能小

原理:表达式vector(m)建立一个临时vector,它是m的一份拷贝:

vector的拷贝构造函数做了这个工作。但是,vector的拷贝构造函数只分配拷贝的元素需要的内存,所以这个临时vector没有多余的容量。然后我们让临时vector和m交换数据,这时我们完成了,m只有临时变量的修整过的容 量,而这个临时变量则持有了曾经在m中的发胀的容量。在这个语句结尾,临时vector被销毁,因此释放了以前m使用的内存。

vector().swap(m); //清除m且最小化它的容量

原理:交换技巧的变体可以用于清除容器和减少它的容量到你的实现提供的最小值。你可以简单地和一个默认构造的临时vector或string做个交换:
string

string(s).swap(s);
string().swap(s);  //清除s且最小化它的容量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值