004-C++基础笔记

2021-03-15  本文已影响0人  千转军师

时间:2021年3月15日16:13:02

参考《Primer C++第五版》

一、基础

1.1 基本变量

(1)算术类型变量

(2)复合类型

int val = 100;
int *val_p = val;
int val = 100;
int &val_ref = val;

1.2 关键字

参考:
https://www.runoob.com/w3cnote/cpp-keyword-intro.html

asm else    new this
auto    enum    operator    throw
bool    explicit    private true
break   export  protected   try
case    extern  public  typedef
catch   false   register    typeid
char    float   reinterpret_cast    typename
class   for return  union
const   friend  short   unsigned
const_cast  goto    signed  using
continue    if  sizeof  virtual
default inline  static  void
delete  int static_cast volatile
do  long    struct  wchar_t
double  mutable switch  while
dynamic_cast    namespace   template

1.3 const关键字

const int a = 10;

默认情况下const修饰的变量仅在本文件中有效
(1)const的引用
又时称为对常量的引用

const int a = 10;
const int &re = a;

也可以引用const变量

int a = 10;
const int &re = a;

(2)常量指针

int a = 10;
const int *p = a;

(3)顶层和底层const
在使用const来修饰指针的时候,可以修饰指针本身,也可以修饰指针指向的变量,前者为顶层const,后者为底层const:

const int a = 10;
const int *p1 = a;  //修饰该指针指向的变量为不可变的
int const *p2 = a;  //修饰指针为不可变的
const int const *p3 = a;  //修饰了指针和指针所指向的变量

1.4 别名

typedef
(1)指针别名

typedef char *type_pointer;   //type_pointer代表的是char *

1.5 自定义的数据结构

类(class)、结构体(struct)
C++库提供的类,例如 std::String

1.6 一个例子

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string str("LL");
    str.append(" OK");
    cout << str;
    return 0;
}

1.7 命名空间

using std;
using pos = std::string::size_type;     //明确值使用某个部分

如果没有声明命名空间,则可以像std::cout 和 std::cin 显式地调用,其中 :: 为作用域。

1.8 标准库的类string

//string调用前提
#include <string>
using std::string

(1)拷贝初始化和直接初始化

string str = "OK";  //  拷贝初始化
string str2("GG");  //直接初始化

(2)常用操作

(3)其他操作
用for处理每一个字符

for (declaration: expression)
    statement

expression表示包含一个序列的对象,declaration用于定义一个变量,该变量会一次表示为序列里的一个个成员,最终完成循环。
(4)例子:转换为大写字母

#include <iostream>
#include <cctype>
#include <string>
using namespace std;
int main(void)
{
    string s("ok, come for me!");
    for(auto &c : s)
      c = toupper(c);
    cout << s << endl;
    return 0;
}

1.8 头文件

形如如cctype头文件,和C语言的 ctype.h 是一样的,但是更符合c++的风格。通常name.h 去掉 .h 然后在前面加上 c来替换。

1.9 标准库类型vector

(1)vector定义和初始化

vector<T> v1
vector<T> v2(v1)
vector<T> v2 = v1
vector<T> v3(n, val)
vector<T> v4(n)
vector<T> v5{a, b, c...}
vector<T> v6={a, b, c...}

(2)操作
添加:push_back方法

#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
    vector<int> v;
    v.push_back(10);
    cout << v[0] << endl;
    return 0;
}

注:如果循环体包含有向vector对象添加元素的语句,则不能使用范围for循环
操作列表:

1.10 迭代器(iterator)

(1)例子:

string s("ok, i am here");
if(s.begin() != s.end())
{
    auto it = s.begin();
    *it = toupper(*it);
}
vector<int>::interator it;
string::interator it2;

迭代器有点类似于指针,假若 it 迭代器指向的是由字符串(string)组成的vector对象,那么

(*it).empty()  // 调用string这个类的empty方法,正确
*it.empty()  //  调用it 迭代器的empty方法,由于it 迭代器没有empty方法,所以出错

注意:但凡使用的迭代器的循环体,都不要向迭代器所属容器添加元素(例如 push_back 方法)
(2)操作

- iter + n
- iter - n
- iter1 += n
- iter1 -= n
- iter1- iter2
- >、>=、<、<=

(3)有序序列实现二分搜索的例子

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(void)
{
    vector<string> vec_str{ "are", "luck", "just", "so so", "why","you"};
    auto beg = vec_str.begin(), end = vec_str.end();
    auto mid = vec_str.begin() + (end - beg)/2;
    string target("so so");
    while(mid != end && *mid != target)
    {
        if(target < *mid)
        {
            end = mid;
        }
        else
        {
            beg = mid + 1;
        }
        mid = beg + (end - beg)/2;  
    }
    cout << *mid << endl;
    return 0;
}

1.11 数组

int a = 10;
int b[a];

(1)初始化例子

int a[3] = {1, 2,};
int a2[3] = {1, 2};
int a3[] = {1, 2,};
int a4[] = {1, 2};
char str[] = "good job";

注:不可以拷贝或者赋值,例如

int a[10], b[10];
a = b;  //错误

1.12 略去部分

1.13 try语句和异常处理

异常处理机制包括异常检测和异常处理,而c++的异常处理包括了:

(1)模式

try{
  [代码块]
}catch(<异常的标识符>){
  [异常处理代码]
}catch(<异常的标识符>){
 [异常处理代码]
}

(2)标准异常
头文件 stdexcept 包含的异常类

#include <iostream>
#include <stdexcept>
using namespace std;
int main(void)
{
    try{
        if(1)
            throw runtime_error("runtime error is comming!");
    }catch(runtime_error){
        cout << "catch a except case";
    }
    
    return 0;
}

1.14 重载函数

二、 类

2.1 类成员函数

#include <iostream>
#include <string>
using namespace std;
class book{
public:
    string name;
    float price;
    string get_name(void){return name;}
    float get_price(void);
};
float book::get_price(void)
{
    return price;
}
int main(void)
{
    book b;
    b.name.append("Three Body");
    b.price = 25.9;
    cout << "name:" << b.get_name() << "  price:"<< b.get_price() <<endl; 
    
    return 0;
}

2.2 构造函数

#include <iostream>
#include <string>
//#include <vector>
//#include <stdexcept>
using namespace std;
class book{
public:
    book(string n);
    string name;
    float price;
};
book::book(string n)
{
    name = n;
}
int main(void)
{
    book b("Three Body");
    cout << "name:" << b.name << endl; 
    
    return 0;
}

2.3 类的拷贝、赋值和析构

编译器有默认的对类的拷贝、赋值和销毁操作,但是有些类的合成版本,会无法工作(特别是分配类对象之外的资源时,合成版本常常失败)。

2.4 访问控制和封装

使用struct和class定义类的唯一区别是默认的访问权限不同。
访问说明符:

2.5 友元

#include <iostream>
#include <string>
using namespace std;
class book{
    friend void show(book);
public:
    book(string n);
private:
    string name;
    float price;
};
book::book(string n)
{
    name = n;
}
void show(book b)
{
    cout << "name:" << b.name << endl;
} 
int main(void)
{
    book b("Three Body");
    show(b);
    
    return 0;
}

2.6 内联成员函数

在类外定义函数成员,前面加上 inline关键词

2.7 类的静态成员

#include <iostream>
using namespace std;
class book{
public:
    static int code;
    string name;
    float price;
};
void show(book b)
{
    cout << "code:" << b.code << endl;
} 
int book::code = 0;
int main(void)
{
    book b1, b2;
    book::code = 10;
    show(b1);
    show(b2);
    return 0;
}

2.8 函数后面的冒号

例如

class cc{
    cc(int);
    int a;  
}
cc::cc(int b):a(b)
{
    ;
}

起到赋值的作用,相当于给成员 a赋值为b


C++标准库


三、IO库

注:

3.1 io库的类和头文件

(1)头文件 iostream

(2)头文件 fstream

(3)头文件 sstream

(4)条件状态
例如

int a;
cin >> a;

如果输入的不是数字,那么返回的错误的状态

while(cin >> a);

3.2 输出缓冲

cout << unitbuff;   //所有输出操作后都会立即刷新缓存区
cout << nounitbuf;  //回到正常的缓冲方式

3.3 文件的输入输出

文件操作

文件打开的模式

#include <fstream>
using namespace std;
int main(void)
{
    fstream fs("aa.txt", ifstream::out);
    fs << "OK";
    return 0;
}

四、顺序容器

类型别名

容器定义和初始化
C c
C c1(c2)
C c1 = c2
C c{a,b,c...}
C c={a,b,c...}
C c(b,e)
C seq(n)
C seq(n,t)

赋值和交换
c1 = c2
c={a,b,c...}
swap(c1,c2)
c1.swap(c2)
seq.assign(b,e)
seq.assign(il)
seq.assign(n,t)

4.1 顺序容器的类别

特点:

4.2 容器操作

4.3 迭代器

迭代器范围: begin、end
包含begin但是不包含end,为左闭合区间: [begin, end)

4.4 顺序容器

(1)添加

emplace函数在容器汇总直接构造元素。

(2)元素访问

(3)元素删除

(4)容器大小更改

注:如果缩小容器,那么可能导致迭代器、指针和引用失效

(5)容器大小管理

五、泛型算法

5.1 算法

5.2 lambda 表达式

例子1:

#include <iostream>
using namespace std;
int main(void)
{
    auto f = [] {return 42;};
    cout << f() << endl;
    return 0;
}

例子2

#include <iostream>
using namespace std;
typedef void (*fun_p)(void);
void fun(int mode, fun_p fp)
{
    cout << "fun:mode=" << mode << endl;
    fp();
}
void fun_call(void)
{
    cout << "fun_call is comming!" << endl;
}
void fun_call2(int type)
{
    cout << "fun_call2: type=" << type << endl;
}
int main(void)
{
    fun(1, fun_call);
    fun(2, [](){fun_call2(2);});
    return 0;
}

5.3 五类迭代器

5.4

算法形参模式

alg(beg, end, other args);
alg(beg, end, dest, other args);
alg(beg, end, beg2, other args);
alg(beg, end, beg2, end2, other args);

六、关联容器

关联容器

无序集合

七、动态内存

智能指针也是模板,类似于vector,所以用法如下:

share_ptr<string> p1;    // share_ptr,可以指向string

7.1 动态数组

#include <iostream>
using namespace std;
int main(void)
{
    int *p = new int[10];
    *p = 10;
    cout << *p << endl;
    return 0;
}

7.2 文本查询例子

(来自书本例子)

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <set>
#include <map>
#include <fstream>
#include <sstream>
using namespace std;

class QueryResult{
    friend std::ostream &print(std::ostream&, const QueryResult&);
    using line_no = std::vector<std::string>::size_type;
    public:
    QueryResult(std::string s,
        std::shared_ptr<std::set<line_no>> p,
        std::shared_ptr<std::vector<std::string>> f):
        sought(s), lines(p), file(f){}
    private:
        std::string sought;
        std::shared_ptr<std::set<line_no>> lines;
        std::shared_ptr<std::vector<std::string>> file;
};

class TextQuery{
public:
    using line_no = std::vector<std::string>::size_type;
    TextQuery(std::ifstream&);
    QueryResult query(const std::string&)const;
private:
    std::shared_ptr<std::vector<std::string>> file;
    std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
    
};

TextQuery::TextQuery(ifstream &is):file(new vector<string>)
{
    string text;
    while(getline(is, text))
    {
        file->push_back(text);
        int n = file->size() - 1;
        istringstream line(text);
        string word;
        while(line >> word)
        {
            auto &lines = wm[word];
            if(!lines)
            {
                lines.reset(new set<line_no>);
            }
            lines->insert(n);
        }
    }
}

QueryResult TextQuery::query(const string &sought)const
{
    static shared_ptr<set<line_no>> nodata(new set<line_no>);
    auto loc = wm.find(sought);
    if(loc == wm.end())
    {
        return QueryResult(sought, nodata, file);       
    }
    else
    {
        return QueryResult(sought, loc->second, file);
    }
    
}

void runQueries(ifstream &infile)
{
    TextQuery tq(infile);
    while(true)
    {
        cout << "enter word to look for, or q to quit:";
        string s;
        if(!(cin >> s) || s == "q") break;
        print(cout, tq.query(s)) << endl;
        
    }
}

ostream &print(ostream & os, const QueryResult &qr)
{
    os << qr.sought << " occurs " << qr.lines->size() << " "
        //<< make_plural(qr.lines->size(), "time", "s") << endl;
        << " times"  << endl;
    for(auto num : *qr.lines)
    {
        os << "\t" << num + 1 << ")"
            << *(qr.file->begin() + num) << endl;
    }
    return os;
    
}

int main(void)
{
    ifstream ifs("t.s");
    ifstream &ifs_r = ifs;
    runQueries(ifs_r);
    
    return 0;
    
}

八、类的拷贝控制

8.1 拷贝、赋值和销毁

特殊的成员函数

8.1 拷贝构造函数

class Foo{
public:
    Foo();  //默认构造函数
    Foo(cost Foo&); //拷贝构造函数
}

合成拷贝构造函数:依次将原对象中的非static成员拷贝到新创建的对象中,不同的成员,如:

8.2 拷贝赋值运算符

重载运算符:运算符本质也是函数
关键字:operator

#include <iostream>
using namespace std;
class Foo{
public:
    int result;
    Foo& operator=(const Foo&);
};
Foo& Foo::operator=(const Foo& a)
{
    result = a.result;
    return *this;
}
int main(void)
{
    Foo f1, f2;
    f1.result = 10;
    f2 = f1;
    cout << "f1:"<< f1.result << "\tf2:" << f2.result << endl;
    return 0;   
}

8.3 析构函数

8.4 使用=default

在类的内部定义成员时,在后面加上=default,合成的函数将隐式地声明为内联的;当成员在类外定义是加上=default,那么合成的函数会当做非内联处理。

8.5 使用=delete

与使用=default, =delete也可以应用在 函数后面,表示不能够被使用,例如:

class Nocopy{
    Nocopy() = defalut; //使用默认的构造函数
    Nocopy(const Nocopy&) = delete; //禁止使用拷贝构造函数
    Nocopy & operator=(const Nocopy &) = delete;    //禁止使用赋值
};

析构函数不能是删除的成员。

8.6 私有的拷贝控制

private 下的拷贝构造函数,普通代码无法访问

8.7 定义行为像值的类

例子;

#include <iostream>
#include <string>
using namespace std::string;

class HasPtr{
public:
    HasPtr(cnst std::string &s = std::string()):
        ps(new std:;string(s)), i(0){}
    HasPtr(const HasPtr &p):
        ps(new std:;string(*p.ps)), i(p.i){}
    HasPtr &operator=(const HasPtr &);
~HasPtr(){delete ps;}
private:    
    std:string *ps;
    int i;
}
HasPtr &operator=(const HasPtr &rhs)
{
    auto newp = new string (*rhs.ps);
    delete ps;
    ps = newp;
    i =rhs.i;
    return *this;
}

int main(void)
{
    return 0;
}

8.8 定义行为像指针的类

增加对指针共享的计数

#include <iostream>
using namespace std;

class HasPtr{
public:
    HasPtr(const std::string &s = std::string()):
        ps(new std::string(s)), i(0), use(new std::size_t(1)){}
    HasPtr(const HasPtr &p):
        ps(p.ps), i(p.i), use(p.use){++*use;}
    HasPtr& operator=(const HasPtr&);
    ~HasPtr();
private:
    std::string *ps;
    int i;
    std::size_t *use;
};
HasPtr::~HasPtr()
{
    if(--*use == 0)
    {
        delete ps;
        delete use;
    }
}
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
    ++*rhs.use;
    if(--*use == 0)
    {
        delete ps;
        delete use;
    }
    ps = rhs.ps;
    i = rhs.i;
    use =rhs.use;
    return *this;
}
int main(void)
{
    return 0;   
}

8.9 交换操作

自定义或者使用库的swap函数

8.10 动态内存分配

std::allocator 类模板是所有标准库容器所用的默认分配器 (Allocator),若不提供用户指定的分配器。

8.11 对象移动

九、操作重载和类型转换

9.1 规则

data1 + data2;
operator+(data1, data2);

【待续】

十、面向对象程序设计

10.1 虚函数

(1)关键词 override 的使用
由于继承类对虚函数的覆盖可能没写正确,一般编译器不报错,就会造成程序的排错很难进行。如果使用override关键词,那么就明确了继承类是要覆盖虚函数的,可以排除更多错误。

class A{
    virtual void fun1(int) const;
    virtual void fun2();
    void fun3();
}
class B : A{
    void fun1(int) const override;  //与基类的fun1匹配
    void fun2(int) override;    //错误:没有匹配项
    void fun3() override;   //错误:fun3不是虚函数
    void fun4() override;   //错误:没有fun4这个虚函数    
}

(2)继承类指定调用基类的虚函数,用作用域符号 ::

A::fun1(var);

(3)抽象基类和纯虚函数

virtual void fun() = 0;

10.2 访问控制和继承

#include <iostream>
using namespace std;
//基类-商品
class Goods{
public:
    double getPrice(void){return price;}
private:
    double price;
};
//书籍类继承与商品类,继承方式是public
class Book : public Goods{
public:
    long long getIsdn(void){return isdn;}
private:
    long long isdn;
    
};
int main(void)
{
    book b;
    return 0;   
}

(1)
public:公共的
protect: 受保护的
private:私有的

(2)规则

(3)派生访问说明符
public、protected、private用于修饰继承,也用于修饰成员变量。

例子:

//注释的代表无访问权限
#include <iostream>
using namespace std;
class Base{
public:
    int base_pub;
protected:
    int base_pro;
private:
    int base_pri;
};
class Sub1 : public Base{
public:
    int fun_pub(void){return base_pub;}
    int fun_pro(void){return base_pro;}
    //int fun_pri(void){return base_pri;}
};
class Sub2 : protected Base{
public:
    int fun_pub(void){return base_pub;}
    int fun_pro(void){return base_pro;}
    //int fun_pri(void){return base_pri;}
};
class Sub3 : private Base{
public:
    int fun_pub(void){return base_pub;}
    int fun_pro(void){return base_pro;}
    //int fun_pri(void){return base_pri;}
};
int main(void)
{
    int t;
    Sub1 s1;
    Sub2 s2;
    Sub3 s3;
    //派生类对象访问自己的成员函数
    t = s1.fun_pub();
    t = s1.fun_pro();
    //t = s1.fun_pri();
    t = s2.fun_pub();
    t = s2.fun_pro();
    //t = s2.fun_pri();
    t = s3.fun_pub();
    t = s3.fun_pro();
    //t = s3.fun_pri();
    //派生类对象访问基类成员
    t = s1.base_pub;
    //t = s1.base_pro;
    //t = s1.base_pri;
    //t = s2.base_pub;
    //t = s2.base_pro;
    //t = s2.base_pri;
    //t = s3.base_pub;
    //t = s3.base_pro;
    //t = s3.base_pri;
    return 0;   
}

(4)改变个别成员的访问权限
关键词 using
例如:

#include <iostream>
using namespace std;
class Base{
public:
    int base_pub;
protected:
    int base_pro;
private:
    int base_pri;
};
class Sub : public Base{
public:
    using Base::base_pro;
};

int main(void)
{
    Sub s;
    s.base_pro = 1;
    return 0;   
}

(4)默认说明符
注: struct和class的唯一差别是默认的成员访问说明符和默认的派生访问说明符的区别。
struct默认的派生说明符是public
class默认的则是private

10.3 拷贝函数和拷贝控制

十一、模板与泛型编程

11.1 模板定义

template <typename T>

11.2 函数模板

例子:

#include <iostream>
using namespace std;

template <typename T> int comp(const T a, const T b)
{
    if(a > b)return 1;
    else return -1;
}
int main(void)
{
    cout << "result:"<< comp(11.9, 20.0);
    return 0;   
}

10.3 模板类型的参数

在模板参数列表中,typename和struct没什么不同。
例子:

template <unsigned N, unsigned M> 
int comp(const char (&p1)[N], const char (&p2)[M])
{
    return strcmp(p1, p2);
}
int main(void)
{
    cout << "result:"<< comp("ok", "lpkj") << endl;
    cout << "result:"<< comp("lpkj", "ok") << endl;
    return 0;   
}

10.4 类模板

(1)样式:

template <typename T> class A{
  //内容
};

例子

#include <iostream>
#include <vector>
using namespace std;
template <typename T> class Foo{
public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    Foo(std::initializer_list<T> il){;}
    T& operator[](size_type i);
private:
}; 
int main(void)
{
    Foo<int> f = {1,2,3,4};
    return 0;   
}

(2)模板类的成员函数类外定义
和普通的成员函数类外定义相比,多了前面的 template <typename T>

template <typename T> class Foo::fun();

(3)类型别名

template<typename T> using twin = pair<T, T>;
twin<string> authors;   //authors是一个 pair<string, string>

10.5 可变参数模板

#include <iostream>
using namespace std;

template <typename...Args> void g(Args ... args){
    cout << sizeof...(Args) << endl;
    cout << sizeof...(args) << endl;
}
int main(void)
{
    g(1,"dfa",2.0);
    return 0;   
}

【待续】

十一、库

11.1 正则表达式

即RE库,定义在regex头文件中

十二、用在大型程序的工具

12.1 异常处

【。。。】

上一篇 下一篇

猜你喜欢

热点阅读