OOP课堂笔记(c++)

这是我面向对象(我没有!)课的笔记,老师讲到哪,我就记到哪,绝不预习!
o( ̄ε ̄*)

现在还没讲完呢。
每周二更新(大概)

封装

内联

不能内联的函数:1.递归函数 2.含有循环的 3.含有开关语句的
一般比较复杂的语句都不会内联

成员函数的类外定义

注意加“类名约束”

this(见下)

初始化列表

对各种成员变量的初始化,除了静态变量(static)。
对常成员(const),引用(&)的初始化必须用初始化列表。
按数据成员在类中定义的顺序赋值,与初始化列表中的顺序无关。

类的大小

只与数据成员有关,与函数成员无关。
(有virtue时增加4b)

构造函数 析构函数

构造函数:
对对象进行初始化,即创建对象的同时进行赋值。
注意 1.名字与类名相同 2.无需指定返回值类型 3.可以重载,可以有参数
不定义的话具有默认构造函数,一旦有重载,默认构造函数将不再发挥作用。

A(){x=0,y=0;}

析构函数:
撤销对象时作用的函数。
注意 1.不允许有参数 2.名字是类名前加"~" 3.无需指定返回值类型

~A(){cout<<"yes"<<endl;}

一个应用:字符串

class A
{
public:
	char *s;
	A(char *t){s=new char[strlen(t)+1]; strcpy(s,t); }
	~A(){if(s) delete s;}
}

私有复制构造函数:只能在类内使用
私有析构函数:不能创建自动对象,撤销对象时应调用类内的destroy(自杀)delete this
私有一般构造函数:只能在类内创建对象,或使用友元

class A
{
private:
	int n;
	A(int n):n(n){}
	~A(){}
public:
	void destory(){ delete this; }
}

private public protected(见下)

friend

外部函数、类、类成员前加friend即可使用private中的成员。

继承

new 与 delete

int main()
{
	int *p1=new int;//p指向一个数,这个数字是随机的
	int *p2=new int(2);//p指向一个数字,这个数字等于2
	int *p3=new int[5];//p指向一个大小为5的数组
	delete p1, p2, []p3;//结束前千万别忘了delete哟
}

private,public

在一个类内:
public:公有成员,对象可直接访问、修改的成员
private:私有成员,在类内可直接访问,但对象无法直接访问
protected:基类对象不能直接调用,但派生类的定义中可以直接调用。

在继承中:
public:公有继承,基类成员类型不变地继承到派生类中
private:私有继承,基类成员类型在派生类中全部变为私有
protected:基类所有public成员全变为protected,其余类型不变。

this

在对象中,this指向该对象。

class A
{
	int x;
public:
	A(int x=0){this->x=x;}
};

static

  • 成员变量的static
  • 成员函数的static
class A
{
	static int b;
public:
	int getb(){return b;}
	static void print(){cout<<"A"<<endl;}
};
class B:public A
{
	static int d;
public:
	int getd(){return d;}
};
/** 下面是static成员变量的初始化 **/
int A::b=0;//或int B::b=0;
int B::d=0;

int main()
{
	A::print();		//使用方法一
	A a; a.print();	//使用方法二
}

using

将基类中的 protected 或 public 调整成 public 或 protected 或 private。
对 private 则没有权限调整。

class A
{
public:
	int x;
protected:
	int y;
};

class B:public A
{
public:
	using A::y;
protected:
	using A::z;
};

赋值兼容/类型相容

公有继承(public)时:

  • 派生类对象赋值给基类对象,赋值基类部分,丢弃派生类增加的部分。
  • 基类指针可指向派生类,但只能执行基类的操作(静态联编)。
class A
{
	int x;
public:
	void set(int x=0){this->x=x;}
	int getx(){return x;}
};

class B:public A
{
	int y;
public:
	void set(int x=0, int y=0){A::set(x);this->y=y;}
	int getx(){return x;}
}

void main()
{
	A a; B b;
	a.set(10); b.set(20,30);
	cout<<a.getx()<<endl;//10
	cout<<b.getx()<<endl;//20
	a=b;
	cout<<a.getx()<<endl;//20

	A *p=&b;
	p->set(40);//这是A类的set(),set(40,50)是错误的。
	cout<<p->getx()<<endl;//40
}

派生类的构造与析构函数

  • 派生类的构造函数写法
  • 析构函数的执行顺序
  • 赋值兼容有关的 delete
class A
{
	int x;
public:
	A(int x=0):x(x){}
	A(A &a):x(a.x){}
	~A(){}
	int getx(){return x;}
};

class B:public A
{
	int y;
public:
	B(int x=0, int y=0):A(x),y(y){}	//构造时先构造基类
	/**下面是赋值构造函数的三种写法**/
	B(B &b):A(b.getx()),y(b.y){}	//朴素型
	B(B &b):A(b),y(b.y){}			//粗犷型,用到赋值兼容,需要是公有继承
	B(B &b):y(b.y){}				//爱咋咋地型。相当于B(B&b):A(),y(b.y){}
	~B(){}		//先执行~B(),再执行成员变量的析构函数(如果有的话),再执行~A()
};

int main()
{
	A *p=new B(1,2);
	delete p;	//只撤销A类对象
}

类图UML

1
图一👆
2
图二👆

3
图三👆

单继承,多继承,重复继承,虚继承(virtual)

单继承:这个派生类只有一个基类
多继承:这个派生类有不止一个基类

重复继承:如上图三的第三张图

class A
{
	int x;
public:
	A(int x=0):x(x){}
	int getx(){return x;}
};

class B:public A
{
	int y1;
public:
	B(int x=0, int y=0):A(x),y1(y){}
	int gety1(){return y1;}
};

class C:public A
{
	int y2;
public:
	C(int x=0, int y=0):A(x),y2(y){}
	int gety2(){return y2;}
};

class D:public B,public C//B,C有不同的A空间
{
	int z;
public:
	D(int a, int b, int c, int d, int e):B(a,b),C(c,d),z(e){}
	int getz(){return z;}
};

int main()
{
	D d(1,2,3,4,5);
	cout<<d.B::getx()<<d.gety1()<<d.C::getx()<<d.gety2()<<d.getz()<<endl;
	//12345
}

虚拟继承:增加关键字virtual,让B、C有同一个A空间

class A
{
	int x;
public:
	A(int x=0):x(x){}
	int getx(){return x;}
};

class B:public virtual A//增加关键字virtual
{
	int y1;
public:
	B(int x=0, int y=0):A(x),y1(y){}
	int gety1(){return y1;}
};

class C:public virtual A//增加关键字virtual
{
	int y2;
public:
	C(int x=0, int y=0):A(x),y2(y){}
	int gety2(){return y2;}
};

class D:public B,public C//B,C有同一个的A空间
{
	int z;
public:
	D(int a, int b, int c, int d, int e):A(a),B(a,b),C(c,d),z(e){}//注意多了A()
	int getz(){return z;}
};

int main()
{
	D d(1,2,3,4,5);
	cout<<d.getx()<<d.gety1()<<d.getx()<<d.gety2()<<d.getz()<<endl;
	//12145
}

重复继承和虚拟继承都属于多继承。

重命名?

多态

相同的消息,发送给不同的对象,产生不同的结果

虚函数

若基类指针指向派生类,用这一指针调用的函数是基类的;如果用虚函数,则可以调用派生类的同名覆盖函数。

举个例子:

class A1
{
public:
	void f(){cout<<"f() from A1"<<endl;}
};

class A2
{
public:
	virtual void g(){cout<<"g() from A2"<<endl;}	//虚函数
};

class B:public A1, public A2
{
public:
	void f(){cout<<"f() from B"<<endl;}
	void g(){cout<<"g() form B"<<endl;}
};

void main()
{
	A1 *p1=new B;
	A2 *p2=new B;
	p1->f(); p2->g();
	delete p1, p2;
}

输出:
f() from A1
g() form B

注意此时 p2 指针调用了派生类 B 的 g()。

析构函数可以是虚函数,构造函数不能是虚函数。
若基类一个函数是虚的,那么派生类中同名覆盖函数默认成虚的。

请问下列调用的分别是哪个类的哪个函数: 普通函数调用虚函数、虚函数调用普通函数、虚函数调用虚函数、构造函数调用虚函数、虚函数调用构造函数、析构函数调用虚函数、虚析构函数调用析构函数、虚函数调用构造函数调用虚函数…
╯(゚Д゚)╯╧╧

吭吭,一般带虚函数的函数调用函数遵循就近原则。
下面就小试一下吧~

class A
{
public:
	void f(){cout<<"f from A"<< endl;}
	virtual void g(){ f(); cout<<"g from A"<< endl;}
};
class B:public A
{
public:
	void f(){cout<<"f from B"<< endl;}
	void g(){ f(); cout<<"g from B"<< endl;}
};
void main()
{
	A *p=new B;
	p->g();
	delete p;
}
输出:
f from B
g from B
class A
{
public:
	virtual void f(){cout<<"f from A"<< endl;}
	void g(){ f(); cout<<"g from A"<< endl;}
};
class B:public A
{
public:
	void f(){cout<<"f from B"<< endl;}
	void g(){ f(); cout<<"g from B"<< endl;}
};
void main()
{
	A *p=new B;
	p->g();
	delete p;
}
	
输出:
f from B
g from A
class A
{
public:
	A(){f();}
	virtual void f(){cout<<"f from A"<< endl;}
};
class B:public A
{
public:
	B():A(){}
	void f(){cout<<"f from B"<< endl;}
	//virtual ~B(){cout<<"del from B"<< endl;}
};
void main()
{
	A *p=new B;
	delete p;
}
输出:
f from A
解释:多态是建立在对象已经构造完成后的状态下的,
由于f()是在构造函数中调用的,virtual还没起作用。
class A
{
public:
	virtual ~A(){cout<<"del from A"<< endl;}
};
class B:public A
{
public:
	~B(){cout<<"del from B"<< endl;}
};
void main()
{
	A *p=new B;
	delete p;
}
输出:
del from B
del from A

对于class A{int x; public: virtual void f(){} };
class B:public A{int y; public: void f(){} };
来说,A、B分别占多大空间呢?(int空间为4)
答案:8、12.
如果没有 virtual 的话,应该是 4、8。
加virtual后生成了虚函数表,这个表首地址,一个void* 类型,就是这4的原因。

既然虚就一虚到底。
纯虚函数virtual void f()=0;
有纯虚函数的类叫抽象类,不能有对象,但可以有指针。

为什么我没有对象——因为我有纯虚函数!

模板

有些函数的参数类型不同,但想做的操作类似。
比如同样是求最大值,但参数分别是 int、double、char、long,难道要对于每种参数都写个函数吗?
程序猿们不想些那么多类似的代码,于是就有了模板。
模板,也称泛型。包括函数模板和类模板。模板有自定义的(自己写的),还有标准的(系统自带的)。
模板不会改变运行效果,只是改变代码结构。

于是就写成这样:

//函数模板定义:
template<typename T>
T GetMax(T a, T b){ return a>b?a:b; }
//使用
int main()
{
	int a, b=1, c=2;
	a=GetMax<int>(b,c);
	a=GetMax(b,c);//依据参数自动判断
}

函数模板由 template 开始,到函数结束结束。
template 是关键字,typename T 是模板参数,typename 是参数类型,T 是形式参数名称(类型参数)。
使用 int a=GetMax<int> 时就将 T 代换成了 int 。<int> 可以不写,除非抽风比较类型不同的两个数。
typename 还可写成 class。如 template <class T>。建议都用 class 。

更多例子:

///例1
template<class T, int SIZE>
T GetMax(T *arr)
{
	T m=arr[0];
	for(int i=1; i<SIZE; ++i)
		if(m<arr[i]) m=arr[i];
	return max;
}

int a[200];
void main()
{
	for(int i=0; i<200; ++i) a[i]=(i*2333)%666;
	int m=GetMax<int,200>(a);
	cout<<m<<endl;
}
///例2
template<class T1, class T2>
T1 Func(T2 a, T2 b){ return a>b?T1(1):T1(0); }

下面说一下类模板。
它长这个样子:

template<class T1, class T2>
class Pair
{
	T1 a; T2 b;
public:
	Pair(){}
	Pair(T1 a, T2 b);
	T1 first(){return a;}
	T2 second();
};
//成员函数的类外定义
template<class T1, class T2>
T2 Pair<T1,T2>::second(){return b;}
//构造函数的类外定义
template<class T1, class T2>
Pair<T1,T2>::Pair(T1 x, T2 y){a=x;b=y;}
//使用
void main()
{
	Pair<int,double> a(1,3.14);
	cout<<a.first()<<" "<<a.second()<<endl;
}

类模板可以有多个类型参数,可提供缺省值(如 <class T=int>),可以有非类型参数,可以有成员模板、还有派生、特化(别问我,我也不知道这是啥)。

我觉得 queue、vector 这样的就是模板吧。

重载

假如定义一个三维向量类:

class Vector
{
	int x, y, z;
public:
	Vector(int x=0, int y=0, int z=0):x(x),y(y),z(z){}
	int getx(){return x;}
	int gety(){return y;}
	int getz(){return z;}
};

我们想求向量的加减乘,于是我们定义下列函数:

Vector add(Vector a, Vector b){return Vector(a.getx()+b.getx(),a.gety()+b.gety(),a.getz()+b.getz()); }
Vector sub(Vector a, Vector b){return Vector(a.getx()-b.getx(),a.gety()-b.gety(),a.getz()-b.getz()); }
int mul1(Vector a, Vector b){return a.getx()*b.getx()+a.gety()*b.gety()+a.getz()*b.getz();}		//点乘
Vector mul2(Vector a, Vector b){return Vector(a.gety()*b.getz()-a.getz()*b.gety(),a.getz()*b.getx()-a.getx()*b.getz(),a.getx()*b.gety()-a.gety()*b.getx()); }		//叉乘

假如现在有 a,b,c,d,e 这几个向量,我们想求 (a+b^c-d)*e,其中 ‘*’ 表示点乘,‘^’ 表示叉乘,那么我们需要写成:

mul2(sul(add(a,mul1(b,c)),d),e)

好繁琐。所以我们想简化表达,想写成

(a+b^c-c)*e

怎么做呢?重载,也就是定义 ±*^ 运算。

这样写:

class Vector
{
	int x, y, z;
public:
	Vector(int x=0, int y=0, int z=0):x(x),y(y),z(z){}
	int getx(){return x;}
	int gety(){return y;}
	int getz(){return z;}
};
Vector operator+(Vector a, Vector b){......}
Vector operator-(Vector a, Vector b){......}
int operator*(Vector a, Vector b){......}
Vector operator^(Vector a, Vector b){......}	

如果实在闲的蛋疼,a+b 也可写成 operator+(a,b) 。

不过好像 ‘^’ 的优先级没 ‘±’ 大,要写成

 (a+(b^c)-c)*e 

重载还没学完,下周再更!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值