CPP 高级

CPP 高级

CPP 的 OOP 初步

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

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;
	thi->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
  • 私有继承(protected
    • 基类的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;
  • 抽象类:至少含有一个纯虚函数的类。
  • 抽象类不可以实例化,必须继承,派生类必须重写纯虚函数。

模板

函数模板

类模板

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值