c++基础-9

隐藏基类函数

class CBase {
public:
    void Print(int) {
        std::cout <<"CBase Print\n";
    }
};

class CDerive : public CBase {
public:
    void Print() {
        std::cout <<"CDerive Print\n";
    }
};

int main()
{
    CDerive d;
    d.Print();
    // d.Print(1); 未继承基类的Print(int),基类此函数被隐藏

    CBase *p = new CDerive;
    p->Print(1);
    // p->Print();  // 基类对象不能调用派生类函数,此处未发生多态
    delete  p;
    return 0;
}

输出结果:
CDerive Print
CBase Print

class CBase {
public:
    void Print(int) {
        std::cout <<"CBase Print\n";
    }
};

class CDerive : public CBase {
public:
    using CBase::Print;   // 显示声明使用基类Print(int),不在被隐藏
    void Print() {
        std::cout <<"CDerive Print\n";
    }
};

int main()
{
    CDerive d;
    d.Print();
    d.Print(1);  // 调用成功

    CBase *p = new CDerive;
    p->Print(1);
    // p->Print();  // 基类对象不能调用派生类函数,此处未发生多态
    delete p;
    return 0;
}

输出结果:
CDerive Print
CBase Print
CBase Print

  • 当基类函数不是虚函数时,派生类中如果有同名的函数,则基类这个函数被隐藏
  • 使用 using 语法可显示继承被派生类隐藏的基类函数

构造继承

// 继承时构造函数不能基层给派生类,常规初始化方式
class CBase {
public:
    CBase() { // 默认构造函数
        m_id = 10;
        std::cout << "m_id : " << m_id << std::endl;
    }
    CBase(int id) : m_id(id) {  // 一个参数的构造函数
        std::cout << "m_id : " << m_id << std::endl;
    }
private:
    int m_id;
};

class CDerive : public CBase {
public:
    CDerive() {   // 调用默认基类构造函数
        m_size = 4;
        std::cout << "m_size : " << m_size << std::endl;
    }
    CDerive(int size) : m_size(size){  // 调用默认基类构造函数
        std::cout << "m_size : " << m_size << std::endl;
    }

    CDerive(int id, int size) : CBase(id), m_size(size) { // 显式调用基类构造函数
        std::cout << "m_size : " << m_size << std::endl;
    }
private:
    int m_size;
};

int main()
{
    CBase base;        // 默认基类构造函数
    CBase base2(20);   // 一个参数的基类构造函数

    CDerive d1;        // 默认派生类构造函数
    CDerive d2(4);     // 一个参数的派生类构造函数
    CDerive d3(30, 8); // 两个参数的派生类构造函数
    return 0;
}

输出结果:
m_id : 10
m_id : 20
m_id : 10
m_size : 4
m_id : 10
m_size : 4
m_id : 30
m_size : 8

  • 继承关系发生时,派生类不能继承基类的构造函数,派生类对象初始化时需要先初始化基类成员变量,在初始化本类中的成员变量,默认调用基类的默认构造函数,也可显式指定基类构造函数的调用
class CBase {
public:
    CBase() { // 默认构造函数
        m_id = 10;
        std::cout << "CBase() m_id : " << m_id << std::endl;
    }
    CBase(int id) : m_id(id) {  // 一个参数的构造函数
        std::cout << "CBase(int id) m_id : " << m_id << std::endl;
    }
private:
    int m_id;
};

class CDerive : public CBase {
public:
    using CBase::CBase;  // 继承基类构造函数
    CDerive(int size) : m_size(size) {
        std::cout << "m_size : " << m_size << std::endl;
    }
private:
    int m_size = 4;
};

int main()
{
    // 构造函数继承规则
    // 1. 最好基类和派生类相同类型的构造函数唯一
    // 2. 如果派生类和基类有相同类型的构造函数,初始化派生类对象时优先调用本类的构造,自动调用基类的无参构造函数(如果存在,若不存在则需要显式指定,否则会出现报错或继承构造冲突)
    // 3. 如果构造函数类型唯一,派生类对象初始化时,如果基类有匹配的构造函数,则调用基类的构造函数
    // 4. 注意继承构造函数的冲突问题

    CDerive d1;        // 调用基类函数初始化
    CDerive d2(30);    // 调用派生类函数初始化,自动调用基类默认无参构造函数,可显式指定基类构造函数
    return 0;
}

输出结果:
CBase() m_id : 10
CBase() m_id : 10
m_size : 30


委托构造

class CBase {
public:
    CBase() : CBase(10) {  // 委托给 CBase(int id) 构造
        std::cout << "CBase() m_id : " << m_id << std::endl;
    }
    CBase(int id) : m_id(id) {  // 一个参数的构造函数
        std::cout << "CBase(int id) m_id : " << m_id << std::endl;
    }
private:
    int m_id;
};

int main()
{
    CBase base;
    return 0;
}

输出结果:
CBase(int id) m_id : 10
CBase() m_id : 10

  • 注意委托构造不能闭环,必须能终止在某个构造函数

左值、右值和右值引用

// 编译时需要关掉编译器优化,qt minGW64编译器,在 *.pro 文件中添加如下两行
// QMAKE_CFLAGS += -fno-elide-constructors
// QMAKE_CXXFLAGS += -fno-elide-constructors
// 清除工程,重新执行 qmake ,重新编译

class CBase {
    CBase()
        : m_data(new int(10)) {
        std::cout << "CBase()" << std::endl;
    }
    CBase(const CBase &other) : m_data(new int(*other.m_data)) {  // 深拷贝构造函数
        std::cout << "CBase(const CBase &)" << std::endl;
    }
    ~CBase() {
        delete m_data;
        std::cout <<"~CBase()" << std::endl;
    }
private:
    int *m_data = nullptr;
};

CBase GetBase() { return CBase(); };

void Print(const CBase&) {}

int main()
{
    Print(GetBase());
    return 0;
}

输出结果:
CBase()
CBase(const CBase &)
~CBase()
~CBase()

// 添加移动构造函数
CBase(CBase &&other) : m_data(other.m_data) {  // 深拷贝构造函数
    std::cout << "CBase(CBase &&other)" << std::endl;
}

输出结果:
CBase()
CBase(CBase &&other)
~CBase()
~CBase()

  • 左值是赋值运算符左面的值,所有的变量或对象都是左值
  • 右值有两个概念,将亡值和纯右值
  • 右值引用就是对一个右值进行引用,格式 T&& ,因为右值不具名,所以只能通过右值引用的方式找到
  • 和左值引用一样,右值引用声明后也必须初始化
  • 右值引用可以优化性能,避免深拷贝
  • 使用 std::move() 函数可以将左值强制转化为一个右值
  • 右值引用类型是独立于值的,右值引用参数作为函数的形参在函数内部再次传递给其它函数时都变成了一个左值
void Print(int&) {  // 左值打印函数
    std::cout << "lvalue\n";
}
void Print(int&&) {  // 右值打印函数
    std::cout << "rvalue\n";
}

void Forward(int&& a) {  // 参数可接受左值或右值
    Print(a);
    Print(std::forward<int>(a));
    Print(std::move(a));
}

int main()
{
    Forward(10);
    int a = 20;
    Forward(std::forward<int>(a));  // 使用左值初始化
    return 0;
}

输出结果:
lvalue
rvalue
rvalue
lvalue
rvalue
rvalue

  • 完美转发是按照参数原来的类型转发给其它函数,c++11 中使用 std::forward() 函数进行完美转发
  • 右值引用做函数参数时,根据初始化值的特性会自动转换为左值或右值
  • c++11 完美转发引入引用折叠规则
// c++98中会报错,引用折叠规则
typedef const int T;
typedef T& TR;
TR& v = 1;
TR类型定义声明 v 的类型v 的实际类型
T&TRA&
T&TR&A&
T&TR&&A&
T&&TRA&&
T&&TR&A&
T&&TR&&A&&
// 一个函数包装器
template <typename F, typename... Args>
inline auto FuncWarpper(F &&f, Args&& ...args) -> decltype (f(std::forward<Args>(args)...)) {
    return f(std::forward<Args>(args)...);
}

void Print0() {
    std::cout << "Print" << std::endl;
}

std::string Print1(const std::string &str) {
    return str;
}

int main()
{
    FuncWarpper(Print0);
    std::cout << FuncWarpper(Print1, "AAA") << std::endl;
    return 0;
}

输出结果:
Print
AAA


function & bind

可调用对象

  • 函数指针
  • 重载小括号运算符的类对象(仿函数)
  • 可被转换为函数指针的类对象
  • 类成员函数指针
void Print() {}
void (*Func)() = &Print;  // 函数指针

struct SBase {
    void operator() () {} // 仿函数
};

struct SPtr {
    using FuncPtr = void(*)();
    operator FuncPtr() {  // 转换为函数指针
        return Print;
    }

    static void Print() {}
    void Print1() {}
};

void (SPtr::*F)() = &SPtr::Print1;  // 类成员函数指针

int main()
{
    return 0;
}

#include <iostream>
#include <functional>   // std::function 头文件

void Print() {}
void (*Func)() = &Print;  // 函数指针

struct SBase {
    void operator() () {} // 仿函数
};

struct SPtr {
    using FuncPtr = void(*)();
    operator FuncPtr() {  // 转换为函数指针
        return Print;
    }

    static void Print() {}
    void Print1() {}
};

void (SPtr::*F)() = &SPtr::Print1;  // 类成员函数指针

int main()
{
    std::function<void()> func; // 定义 func,包装函数格式,返回值 void,形参列表空
    func = Func;
    func = SBase();
    func = SPtr::FuncPtr();
    // func = F;
    return 0;
}
  • c++11 提供可调用对象包装器和绑定器,对各类型的(除过类成员指针之外)可调用对象都可以进行包装,允许保存和延迟调用
  • std::bind 用来将可调用对象和参数进行绑定
#include <iostream>
#include <functional>   // std::function 头文件

int Add(int a, int b) {
    return a + b;
}

int main()
{
    std::function<int(int, int)> func = Add;
    std::cout << "1 : " << func(1, 2) << std::endl;
    std::cout << "2 : " << std::bind(Add, 3, 4)() <<std::endl;  // 直接调用
    std::cout << "3 : " << std::bind(Add, 3, std::placeholders::_1)(5) <<std::endl;  // 部分占位
    std::cout << "4 : " << std::bind(Add, std::placeholders::_1, std::placeholders::_2)(6, 7) <<std::endl; // 完全占位

    func = std::bind(Add, std::placeholders::_1, std::placeholders::_2);  // 保存,延迟调用
    std::cout << "5 : " << func(10, 11) << std::endl;
    std::function<int()> func2;
    func2 = std::bind(Add, 20, 30);
    std::cout << "6 : " << func2() << std::endl;
    std::function<int(int)> func3;
    func3 = std::bind(Add, 30, std::placeholders::_1);
    std::cout << "7 : " << func3(4) << std::endl;

    return 0;
}

输出结果:
1 : 3
2 : 7
3 : 8
4 : 13
5 : 21
6 : 50
7 : 34

#include <iostream>
#include <algorithm>   // 算法头文件
#include <functional>  // std::function 头文件

int main()
{
    std::vector<int> vec;
    for (int i = 0; i < 20; i += 2) {
        vec.emplace_back(i);
        std::cout << vec.at(i / 2) << "\t";
    }
    std::cout << "\n";
    // 使用 bind1st
    // 查找大于 10 的元素个数
    std::cout << "greater 2 count : " << std::count_if(vec.begin(), vec.end(), std::bind1st(std::less<int>(), 2)) <<std::endl;
    // 查找小于 10 的元素个数
    std::cout << "less 2 count : " << std::count_if(vec.begin(), vec.end(), std::bind1st(std::greater<int>(), 2)) <<std::endl;

    // 使用 bind
    // 查找大于 10 的元素个数
    std::cout << "less 2 count : " << std::count_if(vec.begin(), vec.end(), std::bind(std::less<int>(), std::placeholders::_1, 2)) <<std::endl;
    // 查找小于 10 的元素个数
    std::cout << "greater 2 count : " << std::count_if(vec.begin(), vec.end(), std::bind(std::greater<int>(), std::placeholders::_1, 2)) <<std::endl;
    return 0;
}

输出结果:
0 2 4 6 8 10 12 14 16 18
greater 2 count : 8
less 2 count : 1
less 2 count : 1
greater 2 count : 8


lambda

int main()
{
	// 基本 lambda 函数格式
    int a = 10;
    auto func = [a]() -> int { return a; };
    std::cout << func() << "\n";
    return 0;
}

输出结果:
10

  • lambda 来源于函数式编程风格,方便简洁,随时可实现闭包调用
  • [],不捕获任何变量
  • [=],捕获外部作用域中的所有变量,按值捕获
  • [&],捕获外部作用域中的所有变量,按引用捕获
  • [=, &a],捕获外部作用域中的所有变量,按值捕获,对 a 按引用捕获
  • [a],按值捕获 a
  • [&a],按引用捕获 a
  • [this],捕获当前类中的 this 指针,使用了 & 或 = 则自动添加 this 指针捕获
  • lambda 的语法形式如下: [函数对象参数] (操作符重载函数参数) mutable 或 exp 声明 -> 返回值类型 {函数体}
  • lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exp 声明、-> 返回值类型、函数体
int main()
{
    // 在 lambda 函数中修改按值捕获的外部变量
    int a = 10;
    // auto func = [a] { a++; };  // 此处不能修改,编译报错
    [=]() mutable { ++a; }();  // OK

    std::function<void()> func = std::bind([](int a){ a++; }, 10);
    func();

    void (*FuncPtr)(int) = [](int a) { a++; };  // 不捕获任何变量,转换为函数指针
    FuncPtr(10);
}
  • lambda 表达式的类型在 c++11 中被称为“闭包类型”,是一个特殊的,匿名的非 nunion 类型
  • lambda 函数可以和 std::function & std::bind 结合使用
  • 没有捕获任何变量的 lambda 表达式,可以转换为函数指针

tuple

#include <iostream>
#include <tuple>

int main()
{
    // 构造元组,像结构体一样
    std::tuple<int, double, std::string> tp = std::make_tuple(1, 2.5, "id");
    // 获取元素
    int a = std::get<0>(tp);
    // 解包
    double b;
    std::string c;
    std::tie(a, b, c) = tp;
    // 忽略第一个值
    std::tie(std::ignore, b, c) = tp;
}
  • c++11 中新增了可以存储任何类型的元组容器,是一个固定大小的不同类型的值的集合
  • 注意元组的访问方式,只能通过元素类型初始化时所在的位置来访问

可变模板参数

// 基本语法
template <typename... Args>
void Print(Args... args) {
    std::cout << "sizeof... (args) : " << sizeof...(args) << std::endl;  // 打印模板参数个数
}

int main()
{
    Print(1, 2.5, "3");
}
void Print() {  // 递归终止函数
    std::cout << "\nEmpty\n";
}

template <typename T, typename... Args>
void Print(T val, Args... args) {  // 展开函数
    std::cout << val << "\t";
    Print(args...);
}

int main()
{
    // 使用递归方式展开变参
    Print(10, 20.5, "str");  // 函数调用
}

输出结果:
10 20.5 str
Empty

  • c++11中提供了可变参数模板,可以用更加泛化的方式去处理一些问题
  • 注意语法规范及写法,可以用 const、&、&& 等修饰符修饰参数
template <typename T>
void Print(T t) {  // 递归终止函数
    std::cout << t << "\t";
}

template <typename... Args>
void Invork(Args... args) {  // 展开函数
    int arr[] = {(Print(args), 0)...};
}

int main()
{
    // 逗号表达式和初始化列表方式展开变参
    Invork(10, 20.5, "str");  // 函数调用
}

输出结果:
10 20.5 str


default & delete

class CBase {
public:
    // 构造函数默认
    CBase() = default;
    CBase(const CBase &) = default;
    CBase &operator = (const CBase &) = default;
};

class CBase2 {
public:
    // 没有默认的拷贝构造和赋值运算
    CBase2() = default;
    CBase2(const CBase2 &) = delete;
    CBase2 &operator = (const CBase2 &) = delete;
};

class CBase3 {
public:
    // 没有默认的拷贝构造和赋值运算
    CBase3() = default;
private:   // 对外不可见
    CBase3(const CBase3 &);
    CBase3 &operator = (const CBase3 &);
};

int main()
{
    CBase base;
    CBase base2(base);
    CBase2 base3;
    // CBase2 base4(base3);  // 编译不通过,没有拷贝复制构造函数
    CBase3 base5;
    // CBase3 base6(base5);  // 编译不通过,没有拷贝复制构造函数
}
  • 对于构造函数,c++11 使用 default & delete 关键字来声明默认构造或删除构造

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值