类和对象知识点总结

一、类

成员函数可以访问类的私有成员变量
类的封装:将成员变量定义为私有,将成员函数定义为公有,用户只能通过成员函数访问成员变量
定义类对象的方式调用成员函数:类对象+运算符(.)
定义类对象指针动态生成类对象调用成员函数:类对象指针+箭头运算符(->)

二、类成员初始化——构造函数、析构函数与拷贝构造函数

1.通过默认值初始化:int age = 0;
2.通过初始化列表(数据成员是公有的)
3.通过构造函数初始化:构造函数和类同名,没有返回类型

1.构造函数

构造函数在类对象生成时由系统自动调用
用参数初始化表的方式:
Person(int n, char *p):age(n),name(p){}也可以写成:age{n},name{p}
new生成动态对象会调用构造函数,new的返回值为类对象指针
委托构造函数:一个构造函数在其初始化列表中调用了另一个构造函数

public:
	Box(double lv, double wv, double hv): length{lv}, width{wv}, height{hv}{}
	Box(double side):Box(side, side, side){}

2.析构函数

无参数,不能被重载
静态分配内存创建的类对象:在main函数结束时自动被释放,调用析构函数
动态分配内存创建的类对象:类对象在delete时被释放,调用析构函数
delete:先调用析构函数(如果是类对象),再回收动态分配的空间
delete []:先为每个数组元素调用析构函数(如果是类对象),再回收动态分配的空间

	p = new A[10]; //动态生成10个类A的对象
	delete p; //只为第一个类A的对象调用析构函数,然后释放动态空间(错误!)
	delete []p; //为数组中每个类A的对象调用析构函数,再回收动态分配的空间

构造顺序:a, b, *p;析构顺序:*p, b, a

3.拷贝构造函数

Person(const Person & p){...}
使用场合:
Person a = b;
Person c(b);
void f(A var) {}
return a;
拷贝构造函数名与类名相同,形参只能是类对象的引用,不能重载
深拷贝

class A {
private:
	int* p;
public:
	A() { p = new int[2]; }
	~A() { delete[]p; }
	A(const A& a) {
		p = new int[2]; //动态生成新的空间
		for (int i = 0; i < 2; i++) {
			p[i] = a.p[i]; //拷贝每个数值
		}
	}
};

练习:

#include <iostream>
using namespace std;
class A {
public:
	A() { cout << 1; }
	A(const A&) { cout << 2; }
	~A() { cout << 3; }
};
A test() {
	A a;
	return a;
}
int main() {
	A b;
	A c = b;
	test();
	b = test();
	return 0;
}

输出: 121233123333

#include <iostream>
using namespace std;
class A {
public:
	A() { cout << 1; }
	A(const A&) { cout << 2; }
	~A() { cout << 3; }
};
A test(A& a) {
	return a;
}
int main() {
	A b;
	A c = b;
	test(b);
	test(c);
	return 0;
}

输出:12232333

三、类与staic

1.类的静态成员

static 成员属于整个类,由所有类对象共享,在类定义的时候生成并分配空间
定义和初始化必须在类外进行,定义时不再加static
使用类名+(::)访问:A::m = 2;

2.类的静态成员函数

静态成员函数属于整个类,所有类对象都可调用
通过类名+(::)直接调用:A::f1();

1.静态成员函数可以访问静态成员变量静态成员变量和静态成员函数都属于整个类,在类定义的时候已经存在
2.静态成员函数不能访问非静态成员变量非静态成员变量属于某个类对象,而静态成员函数属于整个类,无法确定访问的是哪个类对象的变量
3.非静态成员函数可以访问非静态成员变量非静态成员变量和非静态成员函数同属一个类对象
4.非静态成员函数可以访问静态成员变量静态成员变量被所有类对象共享
5.静态成员函数不能调用非静态成员函数非静态成员函数可能访问了非静态成员变量

类静态成员的应用:创建只有一个实例的类

四、类与const

1.类的常量数据成员:const int m;

2.类的常量成员函数——const放在函数尾部:常量成员函数不能修改类的非static成员变量

class A {
public:
	int m;
	int n;
	void f() const;
};
void A::f() const {
	m = 10; //ERROR,不能对m进行更改!
	cout << a.m;
}

3.返回值为常量的成员函数——const放在函数头部:函数的返回值不能更改

class A {
public:
	int m;
	int n;
	const int& f() { return m; }
};
void main() {
	A a;
	a.f()++; //ERROR! 函数返回值不能修改
	cout << a.m;
}

五、this指针

非静态成员函数返回的是对象本身或对象地址

class A {
public: 
int m;
A* f(){return this;}//返回对象地址
A f1(){return *this;}//返回对象本身
};

六、指向成员的指针

指向指针:<类型名> <类名>::*<指针变量名>;
指向函数:<类型名> (<类名>::*<函数指针名>)(形参表);

#include<iostream>
using namespace std;
class X {
public:
	void f(int);
	int a;
};
void X::f(int x)
{
	a = x;
}
void main() {
	int X::* pmi = &X::a;//指向类成员变量的指针
	void (X:: * pmf)(int) = &X::f;//指向类成员函数的指针
	X objx;
	objx.*pmi = 10;
	cout << objx.a << endl;
	(objx.*pmf)(5);
	cout << objx.a << endl;
}

七、类的友元

允许一些特殊的函数或者访问其他类的私有成员,被称为友元。

函数

1.将普通函数声明为类的友元函数,此函数就可以访问类的私有成员

class A {
private:
	int m;
public:
	friend void f();
};
void f() {//f是普通函数,不属于类A 
	A a;
	a.m = 2;
}

2.友元成员函数

将类B的成员函数声明为类A的友元函数,此函数就可以访问类A的私有成员

//必须先对A进行声明,否则类B里面的f(A &a)报编译错误
class A;
//必须将先定义的类(类B)的成员函数作为后定义类(类A)的友元函数,调换顺序会出现语法错误
class B {
public:
	void f(A& a);
};
class A {
private:
	int m;
public:
	friend void B::f(A& a);
};
//必须将类成员函数f的函数体放在类A定义的后面,因为函数体中a.m = 2访问了类A的成员变量
void B::f(A& a) { a.m = 2; }

1.友元类

将类B声明为类A的友元类,类B所有的成员函数都可以访问类A的任何成员(包括私有成员)

class B;
class A {
private:
	int m;
public:
	friend class B;
};
class B {
public:
	void f() {
		A a;
		a.m = 2;
	}
};

(1) 顺序为:类B的声明,类A的定义,类B的定义
(2) 成员函数f()的函数体必须在类A
单向性:如果类A是类B的友元类,不意味着类B也是类A的友元类
非传递性:如果类A是类B的友元类,类B是类C的友元类,不意味着类A也是类C的友元类

类的对象成员

一个类对象是另一个类的成员

class B {
public:
	int n;
	B(int x) { n = x; }
};
class A {
public:
	B b;//类B的对象是类A的数据成员
	int m;
	A(int x, int y) :b(x) { m = y; }//类A的构造函数中,对b进行初始化(会调用B的构造函数)
};

类成员构造顺序
先按照对象成员的声明顺序执行相应的构造函数,再调用自己的构造函数,析构函数顺序相反

class B {
public:
	int n;
	B(int x) { n = x; }
};
class A {
public:
	int m;
	B b1;
	B b2;
	A(int x1, int x2, int y) :b2(x2), b1(x1) {
		m = y;
	}
};

构造顺序:b1, b2, A自己;析构顺序:A自己,b2, b1
两个类相互包含

class A;//最前面需要放后定义的类的声明
class B {
public:
	A* a;//先定义的类只能包含后定义类的指针或者引用
};
class A {
public:
	B b;//后定义的类可以包含先定义类的对象
};

类不能包含自己的类对象,否则会陷入无限循环,类可以包含自己类的指针或者引用

八、类的运算符重载

运算符重载函数作为类成员函数A operator+(A &r){...}

1.重载‘+’运算符:

‘+’是双目运算符,有两个操作数,一个是调用重载函数的类对象(左操作数),另一个是参数r(右操作数)

2.重载‘++’运算符:

前缀++:返回++之后的值,因此通常返回类对象本身返回类型为引用

class A {
public:
	int n;
	A& operator ++ () {
		n++;
		return *this;
	}
};

后缀++:需要返回++之前的值,因此函数通常返回对象的一个copy(返回类型非引用)(C++语言规定,在后缀++的重载函数的原型参数表中增加一个int 型的无名参数)

class A {
public:
	int n;
	A operator ++ (int) {
		A s = *this;
		n++;
		return s;
	}
};
void main() {
	A a, b;
	a.n = 0;
	b.n = 1;
	cout << (a++).n;
	cout << (++b).n;
	cout << a.n << b.n;
}

输出结果:0 2 1 2

3.重载‘=’运算符

如果返回值不是引用,(a=b)=c 会怎样?
a=b返回的不是a本身,而是一个临时变量,那么(a=b)=c相当于c的值最后没有赋值给a
如果形参不是const会有什么影响?
如果形参是非const类型的引用,实参必须为左值(即不能是表达式),例如,a = b+c 编译会出错
如果形参是const类型的引用,实参可以是非左值,例如,a = b + c 正确

4.运算符重载函数作为类的友元函数

class A {
private:
	int n;
public:
	friend A operator+(A&, A&);
};
A operator+(A& r1, A& r2) {
	A c;
	c.n = r1.n + r2.n;
	return c;
}

本例中重载函数不是类成员函数,‘+’是双目运算符,有两个操作数,因此重载函数有两个参数(左/右操作数)
普通函数无法访问类的私有成员,因此,需要将重载函数声明为类的友元
只能选用友元函数的情形
1.若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数

class A {
	int i;
public:
	A(int x) { i = x; }
	A operator+(const A& a) const {
		return A(i + a.i);
	}
	friend A operator-(const A&, const A&);
};
A operator-(const A& a1, const A& a2) {
	return A(a1.i - a2.i);
}
int main() {
	A a(3), b(4);
	a + b;// ok, a.operator+(b)
	a + 1;// ok, a.operator+(1),1隐式转换为A的对象
	1 + a;// error, operator+的调用者必须为A的对象
	a - b;// ok, operator-(a, b)
	a - 1;// ok, operator-(a, 1),1隐式转换为A的对象
	1 - a;// ok, operator-(1, a),1隐式转换为A的对象
	return 0;
}

2.如果左边的操作数必须是一个不同类的对象,该运算符函数只能作为一个友元函数来实现:对类A重载<<运算符

class A {
	int i;
public:
	A(int x) { i = x; }
	friend ostream& operator<<(ostream& os,
		const A& a);
};
ostream& operator<<(ostream& os, const A& a) {
	return os << a.i;
}
//<<是双目运算符,左边的操作数是ostream类的对象,右边是要输出的类对象
//如果把operator<<重载函数作为类A的成员函数,那么左操作数将是A的对象,无法实现cout在<<左侧
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值