首页 > 其他 > 详细

【CPP】CPP 高级

时间:2020-12-17 15:49:43      阅读:37      评论:0      收藏:0      [点我收藏+]

CPP 高级

面向对象

面向对象的四个重要特性,抽象,封装,继承和多态。而类就是对现实事物的抽象,包括属性和方法。

class 类名
{
public:
    公有成员
protected:
    保护成员 // protected 和 private 的区别就是派生类成员是否可以访问
private:
    私有成员 // 若是直接跟在类名之后定义,则 private 关键字可以省略,即默认为 private
};
class Clock
{
    int hour, minute, second;
public:
    void set_time(int h, int m, int s);
    void show_time();
};

void Clock::set_time(int h, int m, int s)
{
    this->hour = h;
    this->minute = m;
    this->second = s;
}

void Clock::show_time()
{
    printf("%d:%d:%d\n", this->hour, this->minute, this->second);
}

构造函数与析构函数

构造函数

在定义一个对象时,由系统自动调用的初始化函数。构造函数无返回值,且与类同名。

若没有自定义构造函数,编译器会默认生成一个无参的构造函数,其中构造函数可以重载,带默认参数等。但是一旦自定义了构造函数,编译器就不会生成默认的无参的构造函数

拷贝构造函数

拷贝构造函数时一种特殊的构造函数,其参数是本类对象的引用,当用一个对象去初始化另一个对象,函数参数传递,函数返回类对象等时,系统会调用拷贝构造函数。

同构造函数,若没有自定义构造函数,编译器会默认生成一个的拷贝构造函数。

析构函数

当对象内存被释放时被自动调用,同理,编译器也会默认生成一个析构函数。

class Clock
{
    int hour, minute, second;
public:
    Clock(); // 默认的构造函数
    Clock(int h, int m, int s); // 自定义构造函数
    Clock(Clock &c); // 拷贝构造函数
    ~Clock(); // 析构函数
    void set_time(int h, int m, int s);
    void show_time();
};

Clock::Clock()
{
    cout << "默认构造函数被调用" << endl;
}

Clock::Clock(int h, int m, int s): hour(h), minute(m), second(s)
{
    cout << "自定义构造函数被调用" << endl;
}

Clock::Clock(Clock &c)
{
    this->hour = c.hour;
    this->minute = c.minute;
    this->second = c.second;

    cout << "拷贝构造函数被调用" << endl;
}

Clock::~Clock()
{
    cout << "析构函数被调用" << endl;
}

静态属性和静态函数

静态成员以static修饰,其含义不再依附于对象存在,而是属于类本身,是类共有的属性和函数。

  • 静态属性:必须为public,且在类外初始化,用::指明其所属类。在类外初始化时要指明变量类型。
  • 静态函数:可用对象名或类名调用,在函数内部只能使用静态属性。在类外实现时不需要加static
class Clock
{
    int hour, minute, second;
public:
    static int count;
    static int get_count();

    Clock(); // 默认的构造函数
    Clock(int h, int m, int s); // 自定义构造函数
    Clock(Clock &c); // 拷贝构造函数
    ~Clock(); // 析构函数
    void set_time(int h, int m, int s);
    void show_time();
};

int Clock::count = 0;

int Clock::get_count()
{
    return Clock::count;
}

友元关系

友元是 CPP 提供的一种破坏数据封装性的机制。通过声明 B 是 A 的友元类或友元函数,B 可以访问 A 的private属性和函数。

所以友元是基于类的,因为只有类才有private属性。

友元关系是单向的,不可以继承。

友元函数

class Point
{
    int x, y;
public:
    friend float dist(Point a, Point b);
};

float dist(Point a, Point b)
{
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(x*x, y*y);
}

友元类

class A
{
public:
    friend class B;
private:
    int x, y;
};

继承和派生

继承

CPP 支持多继承。

class 派生类名: 继承方式1 基类名1, 继承方式2 基类名2
{
    // xx
};
  • 公有继承(public
    • 基类的publicprotected在派生类中的属性不变,private属性不变。
    • 派生类的成员函数只能访问基类的publicprotected
    • 派生类的对象只能访问基类的public
  • 私有继承(private
    • 基类的publicprotected在派生类中均为privateprivate属性不变。
    • 派生类的成员函数只能访问基类的publicprotected
    • 派生类的对象不可访问任何基类的任何数据。
  • 保护继承(protected
    • 基类的publicprotected在派生类中均为protectedprivate属性不变。
    • 派生类的成员函数只能访问基类的publicprotected
    • 派生类的对象不可访问任何基类的任何数据。

多继承的构造函数与析构函数

  • 由于派生类有多个基类,所以在初始化派生类之前需要对基类也进行初始化。
  • 多继承的构造顺序是按照继承时的顺序,析构函数就是相反的顺序。
  • 若不显式地调用基类的构造函数,系统会自动调用基类缺省的构造函数。
class A
{
    int a;
public:
    A();
    A(int a);
    int get_a(){return a;}
};

A::A()
{
    cout << "A的默认构造函数被调用" << endl;
}

A::A(int a) : a(a)
{
    cout << "A的自定义构造函数被调用" << endl;
}

class B : public A
{
public:
    B();
    B(int a);
};

B::B()
{
    cout << "B的默认构造函数被调用" << endl;
}

B::B(int a): A(a)
{
    cout << "B的自定义构造函数被调用" << endl;
}

同名隐藏规则

当派生类与基类有成员变量同名时:

  • 未指定类名时,默认指派生类成员。
  • 如果非要通过派生类对象访问基类同名成员变量,应指明类名,如a.Base1::var

二义性问题

在多继承时,出现同名成员时,可以通过同名隐藏规则虚基类解决。

class A
{
public:
    void f();
};

class B
{
public:
    void f();
    void g();
};

class C : public A, public B
{

};

// c.f() // 此时,就会出现二义性,可以通过同名隐藏规则解决
c.A::f()
c.B::f()

还有另一种情况,当派生类从多个基类派生,而这些基类又从同一个基基类派生,则在访问基基类所拥有的成员变量时,就会产生二义性,这是需要用到虚基类解决。

class B
{
public:
    int b;
};

class B1 : public B {};
class B2 : public B {};
class C : public B1, public B2 {};

c.b // 出现二义性问题,request for member ‘b‘ is ambiguous

虚基类

虚基类用于解决拥有共同基类的二义性问题。在继承时共同基类时加virtual即可。

虚基类继承时,只为最远的派生类提供唯一的基类成员,不会产生重复。这里的最远派生类是指定义对象时所指定的类的基基类,此时共同成员由最远派生类来初始化。

class B
{
public:
    int nv;
    B(){}
    B(int n){ nv = n; cout << "Member of B" << endl; }
    void fun(){ cout << "fun of B" << endl; }
};

class B1 : virtual public B {};
class B2 : virtual public B {};

class C :public B1, public B2
{
public:
    C(int a) :B(a){ cout << "Member of C" << endl; }
};

c.nv // 1
c.fun() // fun of B

多态

多态是同一事物在不同条件下的执行结果不同。在 CPP 中,实现多态有 3 中方法,函数重载,运算符重载和虚函数。

函数重载

函数名相同,函数的参数个数或者参数类型或者参数顺序不同,则称之为函数重载。

函数的返回值类型不可以作为判断函数重载的依据。

运算符重载

虚函数

CPP 中的子类也可以自动向上转型,即子类可以当做父类用,但是此时只能使用从父类继承的成员,调用的函数也是父类的函数体,没有实现动态绑定。在 CPP 中要实现动态绑定,则需要用到虚函数+指针/引用

  • 虚函数是动态绑定的基础。
  • 虚函数只能是非静态成员函数。
  • 在基类的函数返回类型前加关键字virtual即可。
  • 虚函数只能用在定义,在实现时不可以加。
  • 虚函数具有继承性,后续的重写函数仍然是虚函数。
  • 虚函数必须使用指针或引用调用,否则仍然没有动态绑定。
class A
{
public:
    virtual void fun(){cout << "fun of A" << endl;}
};

class B : public A
{
public:
    void fun() { cout << "fun of B" << endl;}
};

void allfun(A &a)
{
    a.fun();
}

int main(void)
{
    B b;
    allfun(b); // 如果 void allfun(A a),则结果仍为 "fun of A"
    return 0;
}

纯虚函数与抽象类

  • 纯虚函数:虚函数 = 0;
  • 抽象类:至少含有一个纯虚函数的类。
  • 抽象类不可以实例化,必须继承,派生类必须重写纯虚函数。

模板

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。

函数模板

template <typename type1, typename type2>
ret_type func_name(parameter list)
{
   // 函数的主体
}

其中的 type1 和 type2 等是函数所使用的数据类型的占位符名称,这个名称可以在函数定义中使用,代表某一类类型。一个模板里面可以定义多个类型占位符

#include <iostream>
#include <string>

using namespace std;

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

    int i = 39;
    int j = 20;
    cout << "Max(i, j): " << Max(i, j) << endl;

    double f1 = 13.5;
    double f2 = 20.7;
    cout << "Max(f1, f2): " << Max(f1, f2) << endl;

    string s1 = "Hello";
    string s2 = "World";
    cout << "Max(s1, s2): " << Max(s1, s2) << endl;

    return 0;
}

类模板

// 在模板定义语法中关键字 class 与 typename 的作用完全一样
template <class type>
class class_name
{
    // 类声明
}
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>

using namespace std;

template <class T>
class Stack {
  private:
    vector<T> elems; // 元素

  public:
    void push(T const&); // 入栈
    void pop(); // 出栈
    T top() const; // 返回栈顶元素
    bool empty() const { // 如果为空则返回真
        return elems.empty();
    }
};

template <class T>
void Stack<T>::push (T const& elem)
{
    // 追加传入元素的副本
    elems.push_back(elem);
}

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

template <class T>
T Stack<T>::top () const
{
    if (elems.empty()) {
        throw out_of_range("Stack<>::top(): empty stack");
    }
    // 返回最后一个元素的副本
    return elems.back();
}

int main()
{
    try {
        Stack<int> intStack; // int 类型的栈
        Stack<string> stringStack; // string 类型的栈

        // 操作 int 类型的栈
        intStack.push(7);
        cout << intStack.top() <<endl;

        // 操作 string 类型的栈
        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;
}

多线程编程

网络编程

STL 库

【CPP】CPP 高级

原文:https://www.cnblogs.com/zghong/p/14149149.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!