C++常用总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lql0716/article/details/77282152

C++常用总结


author@jason_ql(lql0716)
http://blog.csdn.net/lql0716


1 std:vector

size()指 目前存在的元素数。            元素个数
capacity()指 容器能存储 数据的个数      容器 容量
reserve()指定 容器 能存储数据的个数
resize()  重新 指定 有效元素的个数 ,区别与reserve()指定 容量的大小

1.1 向量vector的复制、拷贝

1vector是一个构造对象,不能直接使用=符号进行复制,必须迭代每个元素来复制。或者重载=操作符。
2、大致有一下几种方法实现用于把一个vector复制给另一个vector:

方法1vector<int > v1(v2);  //声明

方法2:使用swap进行赋值:
vector<int > v1();v1.swap(v2);//将v2赋值给v1,此时v2变成了v1

方法3:使用函数assign进行赋值:
vector<int > v1;//声明v1
v1.assign(v2.begin(), v2.end());//将v2赋值给v1

方法4:使用循环语句赋值,效率较差
vector<int >::iterator it;//声明迭代器
for(it = v2.begin();it!=v2.end();++it){//遍历v2,赋值给v1
    v1.push_back(it);
}

1.2 二维向量初始化

vector<vector<int> > array(m); //这个m一定不能少
//初始化一个m*n的二维数组
for(int i=0;i<m;i++) {
    array[i].resize(n);
}

1.3 向量的其他操作

1 a.size()     //获取向量中的元素个数

2 a.empty()    //判断向量是否为空

3 a.clear()    //清空向量中的元素

4 复制
        a = b ;  //将b向量复制到a向量中

5 比较
        保持 ==、!=、>、>=、<、<= 的惯有含义 ;
        如: a == b ;    //a向量与b向量比较, 相等则返回1

6 插入 - insert

    a.insert(a.begin(), 1000);  //将1000插入到向量a的起始位置前

    a.insert(a.begin(), 3, 1000) ;  //将1000分别插入到向量元素位置的0-2处(共3个元素)

    vector<int> a(5, 1) ;
    vector<int> b(10) ;
    b.insert(b.begin(), a.begin(), a.end()) ;        //将a.begin(), a.end()之间的全部元素插入到b.begin()前

7 删除 - erase
    1、 b.erase(b.begin()) ;    //将起始位置的元素删除
    2、 b.erase(b.begin(), b.begin()+3) ;        //将(b.begin(), b.begin()+3)之间的元素删除

8 交换 - swap
    b.swap(a) ;            //a向量与b向量进行交换
  • 示例
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(){
    std::vector<int> a(5);
    for(int i = 0; i < 5; i++){
        a[i] = i;
        std::cout << "a[" << i << "] = " << i << std::endl;
    }
    a.erase(a.begin());

    std::cout << "\n" << std::endl;
    for(int i = 0; i < 5; i++){
        std::cout << "a[" << i << "] = " << a[i] << std::endl;
    }
}

#输出结果
a[0] = 0
a[1] = 1
a[2] = 2
a[3] = 3
a[4] = 4


a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 4
  • 删除操作.erase()
#include <opencv2\opencv.hpp>
#include <fstream>
#include <vector>

using namespace cv;
using namespace std;

void select(std::vector<int> & vec){
    for (int r = 0; r < vec.size(); r++){
        if (vec[r] % 2 == 0){
            std::vector<int>::iterator it = vec.begin() + r;
            vec.erase(it);
            select(vec);
        }
    }
}

int main(){
    std::vector<int> vec(20);
    for (int i = 0; i < 20; i++){
        vec[i] = i;
        std::cout << vec[i] << std::endl;
    }
    select(vec);
    return 0;
}

1.4 输出向量到txt文件

#include <fstream>
std::vector<Point> kp;
ofstream out("vector.txt", ios::out);
for (int r = 0; r < 20; r++){
    out << kp[r] << "\n" << endl;
}

2 struct 和 typedef struct

3 类型转换

3.1 类型初始化

int a = 0;
float a = 0.0f;
double a = 0.0;
string a = "good";

4 队列容器 queue

  • queue在<queue>头文件中

  • queue与stack非常类似,queue也需要定义两个模板参数,一个是容器类型,一个是元素类型,元素类型是必要的。

  • queue定义:

queue<int> q;queue<double> q2;

  • queue操作:
#include <queue>
int main(){
    queue<int> q;
    q.push(x);  //将x存入队列末端
    q.pop();  //弹出队列的第一个元素,并且不会返回元素的值
    q.front();  //访问队首的元素
    q.back();  //访问队尾的元素
    q.size();  //访问队列的元素个数
    q.empty();  //当队列为空时,返回true
}

5 堆栈 stack

  • stack在<stack>头文件中

  • stack需要定义两个模板参数,一个是容器类型,一个是元素类型,元素类型是必要的,在不指定容器类型时,默认的容器类型为deque

  • stack定义:

stack<int> s;stack<double> s2;

  • stack操作:
#include <stack>
int main(){
    stack<int> s;
    s.push(x);  //将x存入栈
    s.pop();  //出栈,删除栈顶元素,并且不会返回元素的值
    s.top();  //访问栈顶的元素
    s.size();  //访问栈的元素个数
    s.empty();  //当栈为空时,返回true
}

6 unsigned int

  • 整型的每一种都分有无符号(unsigned)和有符号(signed)两种类型(float和double总是带符号的),在默认情况下声明的整型变量都是有符号的类型(char有点特别),如果需声明无符号类型的话就需要在类型前加上unsigned。无符号版本和有符号版本的区别就是无符号类型能保存2倍于有符号类型的正整数数据,比如16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535。由于在计算机中,整数是以补码形式存放的。根据最高位的不同,如果是1,有符号数的话就是负数;如果是无符号数,则都解释为正数。另外,unsigned若省略后一个关键字,大多数编译器都会认为是unsigned int。

  • 无符号整型

size_t

7 static_cast

8 打印 std::cout、std::endl

  • 横向打印
int a[10]={1,2,3}; //初始化前三个元素,其余元素为0
for( int i=0;i<10;i++ )
    cout << a[i] <<" " ;
cout <<endl ; //输出结果:1 2 3 0 0 0 0 0 0 0

9 数学函数

1atan(x)    #表示求的是x的反正切,其返回值为[-pi/2,+pi/2]之间的一个数。

2atan2(y,x)    #求的是y/x的反正切,其返回值为[-pi,+pi]之间的一个数。

3exp(double) 

4pow(double x, double y)    #表示x的y次方

5sqrt(double x)    #表示x开平方

6log(double x)    #表示以e为底,x的对数

7log10(double x)    #表示以10为底,x的对数

8asin(double x)    #反正弦,结果在[-pi / 2, pi / 2]

9acos(double x)    #反余弦,结果在[0, pi]

10sinh(double x), cosh(double x), tanh(double x)    #双曲

10 namespace的用法

//test.h文件
#ifndef TEST_H
#define TEST_H

namespace exm{
    void test1();
};
#endif

//-----------------------------------

// test.cpp文件
#include <iostream>
#include "test.h"
namespace exm{
    void test1(){
        std::cout << "test" << std::endl;
    }
};
#include <iostream>
#include <string>
using namespace std;

//using namespace编译指示,使在C++标准类库中定义的名字在本程序中可以使用
//否则,iostream,string等c++标准类就不可见了,编译就会出错。
//两个在不同命名空间中定义的名字相同的变量

namespace myown1{
    string user_name = "myown1";
}

namespace myown2{
    string user_name = "myown2";
}

int main()
{
    cout<< "/n"
    << "Hello, " 
    << myown1::user_name //用命名空间限制符myown1访问变量user_name
    << "... and goodbye!/n";

    cout<< "/n"
    << "Hello, " 
    << myown2::user_name //用命名空间限制符myown2访问变量user_name
    << "... and goodbye!/n";

    return 0;
}

当然,我们也可以使用程序开头的预编译指示来使用命名空间中的名字。使用预编译指示的好处在于在程序中不必显式地使用命名空间限制符来访问变量。以上主程序可修改为:

int main()
{
    using namespace myown1;
    cout<< "/n"
    << "Hello, " 
    << user_name 
    << "... and goodbye!/n";

    // using namespace myown2;
    cout<< "/n"
    << "Hello, " 
    << myown2::user_name //用命名空间限制符myown2访问变量user_name
    << "... and goodbye!/n";

    return 0;
}

但第二个变量必需用命名空间限制符来访问,因为此时myown1空间中的变量已经可见,如果不加限制,编译器就会无法识别是那一个命名空间中的变量。这一点一定要注意。 以上只是初学者不清楚的一个概念,在以后的文章中还将继续讨论其它的一些概念。
下面写一段完整的程序来表明这个函数的用法;

#include<iostream>
using namespace std;
namespace Cui{
    void Love()
    {
        cout<<"He love a wonderful girl, and her name is SHILI"<<endl;
    }
}

using namespace Cui;

int main()
{
    int a=10;
    cout<<a<<endl;
    Love();
    Cui::Love();    //调用方式2
    return 0;
}

11 模板 template

  • 模板:包括函数模板、类模板

  • 模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现,方便了大规模的软件开发。

  • 对于具有各种参数类型,相同个数,相同顺序的同一函数(重载函数),如果用宏定义来写:
    #define max(a,b) ( (a) > (b) ? (a) : (b) )
    则它不能检查其数据类型,损害了类型的安全性,如果用模板,则为:

template<class T>
T max(T a, Tb){
    return a > b ? a : b;  
}
  • 泛型编程

    • 独立于任何特定类型的方式编写代码
    • 模板用于表达逻辑结构相同,但是具体数据元素类型不同的数据对象的通用行为
  • 模板是泛型编程的基础

    • 通过模板能够快速建立具有类型安全的类库集合和函数集合,使用模板操作不同的数据类型,从而避免需要为每一种数据类型产生一个单独类或者函数
  • 类模板的作用

    • 将程序要处理的对象的类型参数化
    • 使程序可以处理不同类型的对象
  • template

template < class 形参名, class 形参名, ... > 返回类型 函数名(参数列表)
{
    函数体
}
  • 求绝对值的模板示例
#include <iostream>
using namespace std;

template<typename T>T abs(T x){
    return x < 0? -x : x;
}    # typename为类型

int main(){
    int n = -5;
    double d = -5.5;
    cout << abs(n) << endl;
    cout << abs(d) << endl;
}

12 opencv中的智能指针模板类Ptr

13 枚举 enum

14 条件运算符

15 sizeof( int )

sizeof(int) = 每个数据类型长度

16 类的继承 class Child: public Parent{}

17 VS编译之后不关闭cmd窗口(system(“pause”))

#include <opencv2\opencv.hpp>
#include <hash_map>

using namespace cv;
using namespace std;

int main(){
    std::cout << "5" << std::endl;
    system("pause");
    return 0;
}

18 哈希表

  • map
  • hash_map
  • unordered_map
  • unordered_set

19 面向对象

19.1 类 & 对象

  • 参考
    http://www.runoob.com/cplusplus/cpp-classes-objects.html

  • 类是创建对象的模板和蓝图
    类是一组类似对象的共同抽象定义

  • 对象

    对象是类的实例化结果
    对象是实实在在存在的,代表现实世界的某一事物

  • 对象的三大关键特性

    行为:对象能干什么
    状态:对象的属性,行为的结果
    标识:对象的唯一身份

  • 类与对象的区别

    类是静态定义
    对象是动态实例
    程序代码操作的是对象而非类
    建模得到的是类而非对象

  • 类的定义格式

class 类名称{
    public:
        //公有函数
    protected:
        //保护成员
    private:
        //私有函数

        //私有数据成员
        int val;  //数据成员
};

19.1.1 类成员函数

19.1.2 类访问修饰符

19.1.3 构造函数 & 析构函数

  • 类的对象的初始化只能由类的成员函数来进行
  • 建立对象的同时,自动调用构造函数
  • 类对象的定义涉及到一个类名和一个对象名
  • 由于类的唯一性和对象的多样性,用类名而不是对象名来作为构造函数名是比较合适的
  • 默认构造函数(如果没有定义构造函数,则用默认构造函数,默认的构造函数初始化的值为随机的)
  • 构造函数负责对象初始化工作,将数据成员初始化
  • 创建对象时,其类的构造函数确保:在用户操作对象之前,系统保证初始化的进行
    • 建立对象,须有一个有意义的初始值
    • C++ 建立和初始化对象的过程专门由该类的构造函数来完成
    • 构造函数给对象分配空间和初始化
    • 如果一个类设有专门定义构造函数,那么C++就仅仅创建对象而不做任何初始化
  • 构造方法满足以下语法规则:
    • 构造方法名与类名相同
    • 没有返回类型
    • 方法实现主要为字段赋初值
  • 放在外部定义的构造函数,其函数名前要加上“类名::”
  • 构造函数可以有多个
  • 构造函数可以重载
  • 析构函数(~)
  • C++析构函数(Destructor)
    http://c.biancheng.net/cpp/biancheng/view/196.html
  • 一个类可能在构造函数里分配资源,这些资源需要在对象不复存在以前被释放
  • 析构函数也是特殊类型的成员,它没有返回类型,没有参数,不能随意调用,也没用重载。只是在类对象生命期结束的时候,由系统自动调用
  • 析构函数名,就是在构造函数名前面加上一个逻辑非运算符~,表示“逆构造函数”
  • 如果类没有自定义析构函数,则编译器提供一个默认的析构函数

19.1.4 拷贝构造函数

  • 参考
    http://www.runoob.com/cplusplus/cpp-copy-constructor.html

  • 拷贝构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,此形参是对该类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数。

  • 当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式的调用拷贝构造函数
  • 如果一个类没有定义拷贝构造函数,编译器会默认提供拷贝构造函数
class Student{
public:
    Student(const char *pName = "NA", int ssId = 0);
    ~Student();
    Student(const Student &s);  //拷贝构造函数
};

void fn(Student fs){
    //...
}

int main(){
    Student ms;
    Student s2 = ms;  //用一个对象构造另外一个对象
    fn(ms);     拷贝一个临时对象
}
  • 编译器提供的默认拷贝构造函数的行为

    • 执行逐个成员初始化(memberwise initialize),将新对象初始化为原对象的副本
    • 逐个成员,是指编译器将现有的对象的每个非static成员,依次复制到正在创建的对象
  • 浅拷贝

  • 深拷贝

  • 何时需要定义拷贝构造函数

    • 类数据成员有指针
    • 类数据成员管理资源(如打开一个文件)
  • 如果一个类需要析构函数来释放资源,则它也需要一个拷贝构造函数

  • 如果想禁止一个类的拷贝构造,需要将拷贝构造函数声明为private

19.1.5 友元函数(friend void)、友元类(friend class)

class X{
public:
    void initialize();
    friend void g(X, *.int);  //Global friend
    friend void Y::f(x *);  // class member friend
    friend class Z;  //Entire class is a friend
    friend void h();
private:
    int i;
};

19.1.6 内联函数 inline

19.1.7 类的静态成员

19.2 继承

http://www.runoob.com/cplusplus/cpp-inheritance.html

19.3 重载运算符和重载函数

  • 参考:http://www.runoob.com/cplusplus/cpp-overloading.html

  • 重载函数:即函数的参数个数不同或者类型不同

  • 运算符重载
    • 类是用户自定义的数据类型,使用运算符重载可以实现如下逻辑
      • 对象3 = 对象2 + 对象1,如描述复数的类,描述字符串的类
      • 提高程序的可读性
    • 如果一个类没有提供赋值运算符函数,则默认提供一个
    • 如果一个类提供了拷贝构造函数,那么也要提供一个重载的赋值运算函数
    • 重载运算符要保持原运算符的意义
    • 只能对已有的运算符重载,不能增加新的运算符
    • 重载的运算符不会改变原先的优先级和结合性

Person & Person::operate=(const Person & other){
    //检查自赋值
    if(this == & other){
        return  *this;
    }
    //释放原有的内存资源
    delete[] m_data;

    int length = strlen(other.m_data);
    m_data = new char[length+1];
    strcpy(m_data.other.m_data);

    //返回本对象的引用
    return *this;
}
  • 运算符的重载形式
  • 成员函数
  • 友元函数

19.4 多态

  • 参考
    http://www.runoob.com/cplusplus/cpp-polymorphism.html

  • 多态性是指“多种行为”

  • 同样的方法调用而执行不同操作、运行不同代码
  • 多态通过分离做什么和怎么做,从另一个角度将接口和实现进行分离

    “封装”通过合并特征和行为来创建新的数据类型
    “实现隐藏”通过将细节“私有化”把接口和实现进行分离

  • 多态则消除了类型之间的耦合关系

  • LSP(liskov替换原则):子类型必须能够替换掉它们的基类型
  • 多态的概念基于对象引用的动态绑定特性
  • 多态的实现过程

    • 子类重写父类的方法
    • 代码中向父类型变量发出消息(静态绑定)
    • 运行时,根据变量实际引用的对象类型觉得调用哪个方法(动态绑定)
  • 静态绑定在编译期进行

  • 动态绑定在运行期进行
  • 动态绑定是多态现象的根源

  • 虚函数与抽象类

class Animal{
    virtual ~Animal();
    virtual void makeSound();
};
  • 纯虚函数与接口类
// 接口类不能实例化,不能生成对象实例
class Animal{
    virtual ~Animal() = 0;
    virtual void makeSound() = 0;
};
  • 必须为多态基类声明virtual析构函数
  • 针对接口编程,而不是针对实现编程

19.5 数据抽象

19.6 数据封装

  • 参考
    http://www.runoob.com/cplusplus/cpp-data-encapsulation.html

  • 类背后隐藏的思想是数据抽象和封装

  • 信息隐藏,信息隐藏对象的实现细节,不让外部直接访问到

    • 将数据成员和成员函数一起包装到一个单元中,单元以类的形式实现
  • 将数据成员和成员函数包装进类中,加上具体实现的隐藏共同被称作封装,其结果是一个同时带有特征和行为的数据类型

  • 信息隐藏是oop最重要的功能之一,也是使用访问修饰符(public,protected,private)的原因。

  • 信息隐藏的原因包括:

    • 对模块的任何实现细节所作的更改不会影响使用该模块的代码
    • 防止用户意外修改数据
    • 使模块易于使用和维护
  • 除非必须公开底层实现细节,否则应该将所有字段指定为private加以封装

19.7 接口(抽象类)

20 整型转换为字符串

  • 高效率代码1
char* int2str(unsigned int values)
{
    int len = 0;
    const char digits[11] = "0123456789";
    unsigned int tvalue = values;
    while (tvalue >= 100)
    {
        tvalue /= 100;
        len += 2;
    }
    if (tvalue > 10)
        len += 2;
    else if (tvalue > 0)
        len++;

    char* crtn = new char[len + 1];
    crtn += len;
    *crtn = '\0';
    do
    {
        *--crtn = digits[values % 10];
    } while (values /= 10);

    return crtn;
}

http://www.jb51.net/article/109828.htm
  • 高效率代码2
string intstr(double x){
    int k = int(std::round(x));
    string dst4;
    if (k > 0 || k == 0){
        do{
            int b = k % 10;
            k = k / 10;
            char ch = '0' + b;
            dst4 = ch + dst4;
        } while (k > 0);
    }
    else{
        k = -k;
        do{
            int b = k % 10;
            k = k / 10;
            char ch = '0' + b;
            dst4 = ch + dst4;
        } while (k > 0);
        dst4 = "-" + dst4;
    }
    return dst4;
}

21 运算符

  • & 按位与
    如果两个相应的二进制位都为1,则该位的结果值为1,否则为0

  • | 按位或
    两个相应的二进制位中只要有一个为1,该位的结果值为1

  • ^ 按位异或
    若参加运算的两个二进制位值相同则为0,否则为1

  • ~ 取反
    ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0

  • << 左移
    用来将一个数的各二进制位全部左移N位,右补0

  • >> 右移
    将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0

22 整数转换为二进制

二进制位从右端第2位开始,都为1时分别表示:

21=2,22=4,23=824=16,25=32,26=64,27=128,28=256,29=512,....

一个整数由这些2的幂次方相加获得。如

4=22,00000100

43=25+23+21+1,00101011

整数转换为二进制的代码
#include <iostream>
#include<bitset>
using namespace std;


int main()
{
       int a=42486;
       bitset<32> bs(a);
       cout<<bs<<endl;
        system("pause");
        return 0;
}

23 标准库类型string

#include <string>

//字符串的初始化
string s1;    //默认构造函数,s1为空字符串
string s2(s1);    //将s2初始化为s1的一个副本
string s3("value");    //将s3初始化为字符串的副本
string s4(n, 'c');     //将字符串初始化为字符c的n个副本

//字符串的操作
s.empty()    //如果字符串为空,则返回true,否则返回false
s.size()    //返回字符串中字符的个数
s[n]    //返回字符串中的第n个字符,下表从0开始
s1+s2    //将s1和s2连接成一个新的字符串,返回新生成的字符串
s1=s2    //将s1的内容替换为s2的内容
v1==v2    //比较v1和v2的内容,相等则返回true,否则返回false
!=, <, <=, >, >=    //保持这些操作符惯有的含义
  • string类型支持可变长度的字符串

24 动态内存分配

  • 运算符new、delete
  • 在堆上生成对象,需要自动调用构造函数
  • 在堆上生成的对象,在释放时需要自动调用析构函数
  • new / delete, malloc / free 需要配对使用
  • new [] / delete[] 生成和释放对象数组
  • new / delete 是运算符, malloc / free 是函数调用
#include <isotream>
using namespace std;
class Test{
public:
    Test(){ cout << "Test" << endl;}
    ~Test(){ cout << "~Test" << endl; }
private:
    int m_val;
};

int main(){
    Test a;
    cout << "end of }" << endl;
    Test* pVal = new Test();
    delete pVal;
    pVal = NULL;
}

25 const关键字

  • const指定一个不该被改动的对象
    const int BUFFER_SIZE = 512;等价于#difine BUFFER_SIZE 512

  • const限定指针类型

    const出现在星号左边,表示被指物是常量
    const出现在星号右边,表示指针自身是常量

  • const数据成员必须使用成员初始化列表进行初始化

  • const成员函数

    类接口清晰,确定哪些函数可以修改数据成员

  • 使用const提供函数的健壮性

    以pass-by-reference-to-const替换pass-by-value
    控制使用指针和引用传递的实参被意外修改

26 valgrind内存检测工具

27 生成随机数或打乱排序

#include <iostream>
#include <time.h>

std::vector<int> getRandom(int total)
{
    srand((int)time(NULL));
    std::vector<int> input = *new std::vector<int>();
    for (int i = 0; i < total; i++) {
        input.push_back(i);
    }
    std::vector<int> output = *new std::vector<int>();

    int end = total;
    for (int i = 0; i < total; i++) {
        std::vector<int>::iterator iter = input.begin();
        int num = std::rand() % end;
        iter = iter + num;
        output.push_back(*iter);
        input.erase(iter);
        end--;
    }

    return output;
}

//参考:http://www.52study.org/bencandy-49-807-1.html

28 读写txt文件

#include <fstream>
#include <cassert>
#include <iostream>
#include <string>

//读取txt文件,同时将字符串转换为整数
void readTxt(std::string file)
{
    std::ifstream infile;
    infile.open(file);   //将文件流对象与文件连接起来 
    assert(infile.is_open());   //若失败,则输出错误消息,并终止程序运行 

    std::string s,sx, sy;
    while (getline(infile, s))
    {
        std::cout << s << std::endl;
        int k = s.find(",");
        sx = s.substr(0, k);
        std::cout << "sx = " << sx << std::endl;
        sy = s.substr(k + 1, s.length());
        std::cout << "sy = " << sy << std::endl;
        int x, y;
        std::istringstream stream1;
        stream1.str(sx);
        stream1 >> x;

        std::istringstream stream2;
        stream2.str(sy);
        stream2 >> y;
        std::cout << "x = " << int(x) << ",  y = " << int(y) << std::endl;
    }
    infile.close();             //关闭文件输入流 
}

延伸阅读

猜你喜欢

转载自blog.csdn.net/lql0716/article/details/77282152