普通算法题不太涉及的C++知识点(三):封装,对象特性,友元,重载

普通算法题不太涉及的C++知识点(三):封装,对象特性,友元,重载

  • 1. 封装:
    • 1.1 封装的意义:
    • 1.2 访问权限:
    • 1.3 class和struct的区别:
    • 1.4 将成员属性设置为私有:
    • 1.5 类的分文件编写:
  • 2. 对象特性:
    • 2.1 构造函数和析构函数:
    • 2.2 构造函数的分类及调用:
    • 2.3 拷贝构造函数的调用时机:(?)
    • 2.4 构造函数调用规则:
    • 2.5 深拷贝和浅拷贝:
    • 2.6 初始化列表:
    • 2.7 类对象作为类成员:
    • 2.8 静态成员:
      • 2.8.1 静态成员变量:
      • 2.8.2 静态成员函数:
    • 2.9 C++对象指针和this指针
      • 2.9.1 成员变量和成员函数分开存储:
      • 2.9.2 this指针:
    • 2.10 空指针访问成员函数:
    • 2.11 const修饰成员函数:
  • 3. 友元:
    • 3.1 全局函数做友元:
    • 3.2 类做友元:
    • 3.3 成员函数做友元:
  • 4. 运算符重载:
    • 4.1 加号运算符重载:
    • 4.2 左移运算符重载:
    • 4.3 递增运算符重载:
    • 4.4 赋值运算符重载:
    • 4.5 关系运算符重载:
    • 4.6 函数调用运算符重载:

c++面向对象的三大特性:封装,继承,多态。
任何事物都可以为对象,多个相同性质的对象可以抽象称为类。

1. 封装:

1.1 封装的意义:

将属性和行为作为一个整体,表现生活中的物体:

//设计一个圆类:
const double PI = 3.14;

class circle{
//访问权限
public:
//属性
	//半径
	int m_r;
//行为
	//获取周长
	double calZC(){
		return 2*PI*m_r;
	}
};

int main(){
	circle c1;    //实例化:通过一个类创建一个对象;
	c1.m_r = 10;
	cout << "圆的周长为:" << c1.calZC() << endl;
	
	return 0;
}

1.2 访问权限:

公共权限 public:成员 类内可以访问,类外也可以访问。
保护权限 protected:成员 类内可以访问,类外不可以访问。儿子可以访问父亲中的保护内容。
私有权限 private:成员 类内可以访问,类外不可以访问。儿子不可以访问父亲中的私有内容。

class person{
public:
	string name;
protected:
	string car;
private:
	int password;

public:
	void func(){
		name = "alex";
		car = "benz";
		password = 123456;
	}
};

int main(){
	
	person p1;
	p1.name = "Bob";
	//p1.car = "bycicle";  //错误
	//p1.password = 123;  //错误

	return 0;
}

1.3 class和struct的区别:

class和struct的唯一区别:默认访问权限不同。class默认为私有private,struct默认为公有public。

1.4 将成员属性设置为私有:

优点1:可以自己控制读写权限;
优点2:对于写权限,我们可以检测数据的有效性。

class person{
private:
	string name;   //可读可写
	int age;       //可读可写有限制
	string lover;  //只写

public:
	void setname(string str){
		name = str;
	}
	string setname(){
		return name;
	}

	int getage(){
		return age;
	}
	void setage(int n){
		if(age<0 || age>150){
			age = 0;
			cout << "error" << endl;
			return;
		}
		age = n;
	}

	void setlover(string str){
		lover = str;
	}
};

int main(){
	
	person p;
	p.setname("alex");
	cout << p.getname() << endl;


	return 0;
}

1.5 类的分文件编写:

Point.h:

class Point{
private:
	int m_x;
	int m_y;
public:
	void setx(int x);
	int getx();
	void sety(int y);
	int gety();
};

Point.cpp:

#include "Point.h"
void Point::setx(int x){
	m_x = x;
}
int Point::getx(){
	return m_x;
}
......
......

int main(){

	return 0;
}

2. 对象特性:

2.1 构造函数和析构函数:

构造函数和析构函数由编译器提供。
构造函数:类名(){}

  1. 没有返回值也不写void;
  2. 函数名称和类名相同;
  3. 可以有参数,因此可以重载;
  4. 程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次。

析构函数:~类名(){}

  1. 没有返回值也不写void;
  2. 函数名称和类名相同,名称前加~;
  3. 不可以有参数,因此不可以重载;
  4. 程序在对象销毁时会自动调用析构,无须手动调用,而且只会调用一次。
class person{
public:
	person(){
		cout << 1 << endl;
	}
	~person(){
		cout << 2 << endl;
	}
};

void test(){
	person p1;
}

int main(){

	test();     //会输出1和2;

	person p2;  //只输出1;
	
	return 0;
}

2.2 构造函数的分类及调用:

两种分类方式:

  1. 按照参数:有参构造和无参构造;
  2. 按照类型:普通构造和拷贝构造。

三种调用方式:

  1. 括号法;
  2. 显示法;
  3. 隐式转换法。
class person{
public:
	//普通无参构造
	person(){
		//cout << 1 << endl;
	}
	//普通有参构造
	person(int a){
		m_age = a;
		//cout << 1 << endl;
	}
	
	//拷贝构造
	person(const person &p){
		m_age = p.m_age;
	}

private:
	int m_age;
};

//调用方式:
void test(){
	//括号法
	person p1;       //调用普通无参构造,不要加小括号;
	person p2(10);   //调用普通有参构造;
	person p3(p2);   //调用拷贝构造;

	//显示法
	person p1;
	person p2 = person(10);
	person p3 = person(p2);
	//person(10)和person(p2)是匿名对象,当前行结束后自动回收;

	//隐式转换法
	person p4 = 10;
	person p5 = p4;
}

int main(){

	return 0;
}

2.3 拷贝构造函数的调用时机:(?)

class person{
public:
	//普通无参构造
	person(){
		//cout << 1 << endl;
	}
	//普通有参构造
	person(int a){
		m_age = a;
		//cout << 1 << endl;
	}
	
	//拷贝构造
	person(const person &p){
		m_age = p.m_age;
	}

private:
	int m_age;
};

void test(){
	person p1(20);
	person p2(p1);
}

int main(){
	test();
	return 0;
}

2.4 构造函数调用规则:

如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造;
如果用户定义拷贝构造函数,C++不会再提供其他构造函数。

2.5 深拷贝和浅拷贝:

浅拷贝:简单的赋值拷贝操作。
深拷贝:在堆区重新申请空间,进行拷贝操作。

如果在类内使用new在堆区申请了空间,要在析构函数内使用delete手动释放空间。
浅拷贝的问题:堆区内存重复释放。

解决办法:使用深拷贝。

class person{
public:
	//深拷贝
	person(const person &p){
		m_age = p.m_age;
		m_height = new int(*p.m_height);
	}

	//析构函数
	~person(){
		if(m_height!=NULL){
			delete m_height;
			m_height = NULL;
		}
	}

private:
	int m_age;
	int *m_height;
};

2.6 初始化列表:

C++提供了初始化列表语法,用于初始化属性:

class person{
public:
	//传统初始化操作:
	//person(int a, int b, int c){
	//	m_a = a;
	//	m_b = b;
	//	m_c = c;
	//}

	//初始化列表:
	person(int a, int b, int c):m_a(a), m_b(b), m_c(c){}

	int m_a;
	int m_b;
	int m_c;
};

void test(){
	person p1(1,2,3);
}

int main(){
	test();
	return 0;
}

2.7 类对象作为类成员:

class phone{
public:
	phone(string name){
		m_name = name;
	}
	string m_name;
};

class person{
public:
	person(string name, phone ph):m_name(name), m_phone(ph){}

	string m_name;
	phone m_phone;
};

void test(){
	person p1("alex", "iphone");
}

int main(){
	test();
	return 0;
}

构造时先构造phone,再构造person;析构时相反。

2.8 静态成员:

静态成员就是在成员变量和成员函数之前加上关键字static,称为静态成员。
静态成员分为静态成员变量和静态成员函数:

2.8.1 静态成员变量:

所有对象共享同一份数据;
在编译阶段分配内存;
类内声明,类外初始化。

class person{
public:
	static int m_a;  //静态成员变量,类内声明

private:
	statci int m_b;
};
int person::m_a = 100; //类外初始化
int person::m_b = 200;

void test(){
	person p;
	cout << p.m_a << endl;  //输出100

	person p2;
	p2.m_a = 200;
	cout << p.m_a << endl;  //输出200,因为m_a是共享的
}

void test2(){
	//静态成员变量有两种访问方式:
	//1.通过对象进行访问:
	person p;
	cout << p.m_a << endl;

	//2.通过类名进行访问:
	cout << person::m_a << endl;
}

void test3(){
	cout << person::m_b << endl; //错误,静态成员变量也有访问权限
}

int main(){
	test();
	return 0;
}

2.8.2 静态成员函数:

所有对象共享同一个函数;
静态成员函数只能访问静态成员变量。

class person{
public:
	static void func(){
		m_a = 100;
		//m_b = 200;  //报错,因为静态成员函数只能访问静态成员变量
		cout << 1 << endl;
	}

	static int m_a;
	int m_b;
};

//静态成员函数的两种访问方式:
void test(){
	//1.通过对象访问:
	person p;
	p.func();

	//2.通过类名访问:
	person::func();
}

int main(){
	test();
	return 0;
}

2.9 C++对象指针和this指针

2.9.1 成员变量和成员函数分开存储:

只有非静态成员变量才属于类的对象。

class person{
public:
	int m_a;
	static int m_b;
	void func(){}
};

//静态成员函数的两种访问方式:
void test(){
	person p;
	cout << sizeof(p) << endl;  
	//空对象占用的内存空间为1,有1个int变量的对象占用的内存空间为4,加成员函数或静态成员变量不影响对象占用的内存空间。
}

int main(){
	test();
	return 0;
}

2.9.2 this指针:

this指针不需要定义,可以直接使用。
用途:
当形参和成员变量同名时,可用this指针来区分;
在类的非静态成员函数中返回对象本身时,可使用return *this。

class person{
public:
	person(int age){
		//age = age;  //这样写发生了混淆
		//方法一,不用同样的名称
		//方法二:
		//this指针指向 被调用的成员函数 所属的对象
		this->age = age;
	}

	person& add(person &p){
		this->age += p.age;
		return *this;
	}

	int age;
};

void test(){
	person p1(18);
	cout << p1.age << endl;
}

void test2(){
	person p2(10);
	p2.add(p1).add(p1).add(p1).add(p1);  //链式编程
	cout << p2.age << endl;
}

int main(){
	test();
	test2();
	return 0;
}

2.10 空指针访问成员函数:

class person{
public:
	void show(){
		cout << 1 << endl;
	}

	void showage(){
		if(this==NULL){   //防止传入空指针导致出错;
			return;
		}
		cout << m_age << endl;
	}
	
	int m_age;
};

void test(){
	person *p = NULL;
	p->show();       //正确
	//p->showage();  //错误
}

int main(){
	test();
	return 0;
}

2.11 const修饰成员函数:

常函数和常对象:

class person{
public:
	//this指针的本质是指针常量,指针的指向是不可以修改的,但指向的值是可以修改的。
	//常函数:
	void change() const{   //这个const使指向的值也不可修改了
		m_a = 200;  //错误
		m_b = 100;  //正确,因为m_b是mutable变量
	}
	
	void func(){
		;
	}

	int m_a;
	mutable int m_b;
};

void test(){
	//常对象:
	const person p1();  
	p1.m_a = 100;  //错误
	p1.m_b = 200;  //正确

	//常对象只能调用常函数,因为普通成员函数可以修改属性:
	p1.func();     //错误
	p1.change();   //正确
	
}

int main(){
	test();
	return 0;
}

3. 友元:

友元的目的是让一个函数或者类,能够访问另一个类的私有成员。

3.1 全局函数做友元:

class building{
friend void goodfriend(building *b1);
public:
	building(){
		m_sittingroom = "客厅";
		m_bedroom = "卧室";
	}

	string m_sittingroom;
private:
	string m_bedroom;
	
};

//全局函数做友元:
void goodfriend(building *b1){
	cout << b1->m_sittingroom << endl;  //一直可以访问;
	cout << b1->m_bedroom << endl;      //不能直接访问,除非使用friend;
}

int main(){
	building b1;
	goodfriend(&b1);
	return 0;
}

3.2 类做友元:

class building{
	friend class goodfriend;  //第二行
public:
	building();

	string m_sittingroom;
private:
	string m_bedroom;
};
//类外写成员函数:
building::building(){
	m_sittingroom = "客厅";
	m_bedroom = "卧室";
}


class goodfriend(){
public:
	goodfriend();
	void visit();

	building *b;
};
goodfriend::goodfriend(){
	b = new building;
}
void goodfriend::visit(){
	cout << b->m_sittingroom << endl;  //可以直接访问;
	cout << b->m_bedroom << endl;      //不能直接访问,除非写了第二行;
}

int main(){
	goodfriend g1;
	g1.visit();
	
	return 0;
}

3.3 成员函数做友元:

class building{
	friend void goodfriend::visit();  //第二行
public:
	building();

	string m_sittingroom;
private:
	string m_bedroom;
};

class goodfriend(){
public:
	void visit();
	void visit2();
};

int main(){
	
	return 0;
}

4. 运算符重载:

4.1 加号运算符重载:

成员函数重载:

class person{
public:
	person operator+(person &p){
		person temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + p.m_b;
		return temp;
	}

	int m_a;
	int m_b;
};

全局函数重载:

class person{
public:
	person operator+(person &p1, person &p2){
		person temp;
		temp.m_a = p1.m_a + p2.m_a;
		temp.m_b = p1.m_b + p2.m_b;
		return temp;
	}

	//函数重载:
	person operator+(person &p1, int num){
		person temp;
		temp.m_a = p1.m_a + num;
		temp.m_b = p1.m_b + num;
		return temp;
	}


	int m_a;
	int m_b;
};

运算符重载也可以发生函数重载。

4.2 左移运算符重载:

作用:可以输出自定义数据类型。

class person{
	friend ostream& operator<<(ostream &cout, person &p);
public:
	//不用成员函数来实现左移运算符重载;
	//只用全局函数来重载:
	ostream& operator<<(ostream &cout, person &p){
		cout << m_a << " " << m_b << endl;
		return cout;
	}

	int m_a;
	int m_b;
};

int main(){

	person p1;
	p1.m_a = 10;
	p1.m_b = 20;
	//cout << p1;  //返回void时,不能在后面追加 <
	cout << p1 << endl;
	
	return 0;
}

4.3 递增运算符重载:

前置递增返回引用,后置递增返回值:

class myinteger{
	friend ostream& operator<<(ostream& cout, myinteger myint);
public:
	myinteger(){
		m_num = 0;
	}

	//重载前置++运算符,返回引用是为了一直对一个数据进行操作
	myinteger& operator++(){
		m_num++;
		return *this;
	}

	//重置后置++运算符,不能返回引用
	myinteger operator++(int){  //int代表占位参数,可以用于区分前置和后置递增
		myinteger temp = *this;
		m_num++;
		return temp;
	}

private:
	int m_num;
};

ostream& operator<<(ostream& cout, myinteger myint){
	cout << myint;
	return cout;
} 

int main(){

	myinteger myint;
	cout << ++(++myint) << endl;  //加引用后返回2,不加则返回1
	cout << (myint++) << endl;
	
	return 0;
}

4.4 赋值运算符重载:

class person{
public:
	
	person(int age){
		m_age = new int(age);
	}
	~person(){
		if(m_age!=NULL){
			delete(m_age);
			m_age = NULL;
		}
	}

	person& operator=(person &p){
		//编译器提供的是浅拷贝:
		//m_age = p.m_age;
		
		if(m_age!=NULL){
			delete(m_age);
			m_age = NULL;
		}
		//深拷贝:
		m_age = new int(*p.m_age);
		//返回对象本身:
		return *this;
	}

	int *m_age;
};

void test(){
	person p1(18);
	person p2(20);
	person p3(30);
	p3 = p2 = p1;

	cout << *p1.m_age << endl;
	cout << *p2.m_age << endl;
	cout << *p3.m_age << endl;
}

int main(){
	test();
	return 0;
}

4.5 关系运算符重载:

class person{
public:
	person(string name, int age){
		m_name = name;
		m_age = age;
	}

	bool operator==(person &p){
		if(m_name==p.m_name && m_age==p.m_age){
			return true;
		}
		return false;
	}

	string m_name;
	int m_age;
};

void test(){
	person p1("Tom", 18);
	person p2("Tom", 18);
	if(p1==p2){
		cout << 1 << endl;
	}
}

int main(){
	test();
	return 0;
}

4.6 函数调用运算符重载:

函数调用运算符()也可以重载;
由于重载后使用的方式非常像函数的调用,因此称为仿函数;
仿函数没有固定写法,非常灵活。

class myprint{
public:
	void operator()(string test){
		cout << test << endl;
	}
};

void func(string test){
	cout << test << endl;
}

void test(){
	myprint p1;
	p1("Hello world");
	func("Hello world");
}

int main(){
	test();
	return 0;
}

你可能感兴趣的:(C++知识点)