C++ class学习笔记

C++ class

1.C++的默认权限是私有private

#include <iostream>
using namespace std;

class hahaha {
	int m_a;//默认权限是私有
};

struct haha
{
	int m_a;
};

int main()
{
	//在C++中 struct和class的区别是默认权限不同
	//struct的默认权限是公共 public
	//class的默认权限是私有 private
	//共有和私有的区别是 在类外能否访问
	
	hahaha K;
	K.m_a = 39;//报错,说明hahaha内m_a是私有对象 在类外不能访问
	haha O;
	O.m_a =  89;//
	return 0;	
}

2.成员属性设置私有的好处

可以自己控制权限

对于写的东西可以检测数据有效性

3.成员属性设置为私有的做法

#include <iostream>
using namespace std;

#include <string>

class xixi 
{
private:
	string m_name;//姓名 只读 不可以访问和改变
	int m_age;//年龄 只读

//若要可以改写私有对象,可以在public里完成	
public:
	void setname(string name)
	{
		m_name = name;
	}//可写
	
	string getname()
	{
		return m_name;
	}//可读
};

int main()
{
	xixi haha;
	haha.setname("skanha");
	haha.getname();
	cout<<"姓名"<<haha.getname()<<endl;
	return 0;
}

3.在一个类中可以引用另外一个类

4.表明一个对象的作用域,利用::来实现

比如一个函数add是在class A内起作用,则可以这样做

int A::add(int a,int b)
{

}

5.构造函数和析构函数

编译器会自动调用这两个函数,如果没有调用,系统会自动调用

5.1构造函数 初始化 类名(){}

1.构造函数没有返回值也不用写void

2.函数名称和类名相同

3.可以有参数,可能发生重载

4.程序在调用对象的时候会自动调用构造,无需手动调用,且只调用一次

5.1.1构造函数分类及调用

分类:

​ 按参数:有参和无参(默认)

​ 按类型:普通和拷贝

//拷贝函数
类名(const &具体的类 比如xixi a)//把xixi a 拷贝

调用方式:

​ 括号、显示、隐式转换法

a.拷贝构造函数的调用时机

使用一个已经创建完毕的对象来初始化一个新对象

值传递方式给函数参数传值

值方式返回局部对象

#include <iostream>
using namespace std;

#include <string>
class xixi 
{

private:
	string m_name;//姓名 只读

//若要可以改写私有对象,可以在public里完成	
public:
	//构造函数
	xixi()
	{
		cout<<"默认构造"<<endl;
	}
	xixi(int a)
	{
		cout<<"参数调用构造函数"<<endl;
		m_age = a;
	}
	xixi(const xixi &p)
	{
		cout<<"拷贝调用构造函数"<<endl;
		m_age = p.m_age;
	}
	//析构函数
	~xixi()//对象被销毁前会调用
	{
		cout<<"析构"<<endl;
	}
	int m_age;//年龄
};

//使用一个已经创建完毕的对象来初始化一个新对象
void test ()
{
	xixi p1(20);
	xixi p2(p1);
	cout<<"p2 年龄 "<<p2.m_age<<endl;
}

//值传递方式给函数参数传值
void dowork(xixi p)
{
	p.m_age = 4;
}

void test2()
{
	xixi p;
	dowork(p);
	cout<<"p的值" <<p.m_age <<endl;
}

//值方式返回局部对象
xixi dowork2()
{
	xixi p1;
	return p1;
}

void test3()
{
	xixi p = dowork2();
	
}

int main()
{
	test();
	test2();
	test3();
	system("pause");
	return 0;
}

*深拷贝与浅拷贝

浅拷贝:简单的赋值操作,c++自己创造的拷贝构造函数就是一个浅拷贝

深拷贝:在堆区重新申请空间,进行拷贝操作,如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

#include <iostream>
using namespace std;

class xixi
{
public:
	int m_age;
	int* m_height;
	xixi(int age,int height)
	{
		m_age = age;
		m_height = new int(height);
	}
	xixi(const xixi& p)
	{
		cout << "kaobei" << endl;
		m_age = p.m_age;
        //m_height = int p.m_height; 浅拷贝
		m_height = new int (*p.m_height);//深拷贝

	}
	~xixi()
	{
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
	}
};

void test01()
{
	xixi p1(18, 30);
	xixi p2(p1);
	cout << "年龄和身高分别为" << p2.m_age << "  " << *p2.m_height << endl;
}

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

5.2析构函数 清理 ~类名(){}

1.析构函数没有返回值也不用写void

2.函数名称和类名相同,前面加~

3.不可以有参数,不可以发生重载

4.程序在调用对象的时候会自动调用析构,无需手动调用,且只调用一次

#include <iostream>
using namespace std;

#include <string>

class xixi 
{

private:
	string m_name;//姓名 只读
	int m_age;//年龄 只读

//若要可以改写私有对象,可以在public里完成	
public:
	void setname(string name)
	{
		m_name = name;
	}
	
	string getname()
	{
		return m_name;
	}
	//构造函数
	xixi()
	{
		cout<<"构造"<<endl;
	}
	//析构函数
	~xixi()//对象被销毁前会调用
	{
		cout<<"析构"<<endl;
	}
};

int main()
{
	xixi haha;
	haha.setname("skanha");
	haha.getname();
	cout<<"姓名"<<haha.getname()<<endl;
	return 0;
}

运行结果如下

在这里插入图片描述

初始化列表

class Person
{
public:
	int m_a, m_b, m_c;
	//传统初始化
	/*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)
	{

	}
};

void test01()
{
	Person p(39,39,10);
	cout << "m_a=" << p.m_a << endl;
	cout << "m_b=" << p.m_b << endl;
	cout << "m_c=" << p.m_c << endl;
}


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

类对象作为类成员

当其他类对象作为本类成员,构造时先构造类对象,再构造自身。其他类对象可以看作一个人的器官,而要先有器官,才能组成完整的人,可以类比构造顺序。

而析构的顺序与构造相反。

静态成员

1.静态成员变量

a.类内声明,类外初始化

b.静态成员变量,不属于某个对象,所以对象都共享一份数据

访问方式:通过对象访问,通过类名访问。

c.同时静态成员变量也是有访问权限的,当成员变量为private时,私有权限类外访问不到。

class A
{
public:
	static int m_a;
};

int A::m_a = 6;

int main()
{
	A p;
	A p2;
	p2.m_a = 8;
	//通过对象访问
	cout << p.m_a << endl;
	//通过类名进行访问
	cout << A::m_a << endl;
	return 0;
}

2.静态成员函数

a.所有对象共享同一个函数。

b.静态成员函数只能访问静态成员变量。

c.有访问权限,在private的静态成员函数在类外不能调用。

class haha
{
public:
	//静态成员函数
	static	void func()
	{
		m_a = 100;//静态成员函数可以访问静态成员变量
		//m_b = 200;//会报错,这说明静态成员函数不能访问非静态成员变量,无法区分到底时哪个对象的m_b
		cout << "static 调用" << endl;
	}
	static int m_a;//静态成员变量
	//int m_b;
};

void test1()
{
	//通过对象访问
	haha p;
	p.func();
	//通过类名访问
	haha::func();
}

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

只有非静态的成员变量才属于类的对象上。静态成员变量,非静态成员函数,静态成员函数都不属于类对象上。

空对象占有内存空间为1,每个空对象也应该有独一无二的内存你地址,分配一个字节的空间,是为了区分每个空对象占内存的地址

this指针

this指针指向被调用的成员函数所属的对象

this指针隐含在每一个非静态成员函数内的一种指针,且不需要被定义。

可以解决名称冲突,返回对象

this指针本质是指针常量,指针的指向不可以修改

class A
{
public:
	A(int age)
	{
		//this 指针指向被调用成员函数所属的对象
		//当形参和成员变量同名时,可以用this指针区分
		this->age = age;
	}
	A& ADD(A &p)
	{
		this->age += p.age;
		//返回对象自己用*this 返回对象本身
		return *this;
	}
	int age;
};

void test1()
{
	//1.解决名称冲突
	A p(5);
	cout << p.age << endl;
}

//2.返回对象本身用*this
void test2()
{
	A p1(34);
	A p2(10);
	//链式编程思想
	p2.ADD(p1).ADD(p1);
	cout << p2.age << endl;
}

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

const修饰成员函数

1.常函数 在函数后加const 比如 void test01()const {}

a.成员函数后加const后称这个函数为常函数

b.常函数内不可以修改成员属性

c.成员属性声明时加关键字mutable后,在常函数中依然可以修改

2.常对象

a.声明对象前加const称该对象为常对象

b.常对象只能调用常函数

友元

目的:让一个函数或者类访问另一个类中私有成员

关键字:friend

三种实现:全局函数做友元 类做友元 成员函数做友元

//全局函数做友元
class Building
{
//告诉编译器,func是Building类的好朋友,在类外可以访问私有成员
friend void func();
};
void func()
{
    
}

类与成员函数做友元的方法与全局函数做友元类似。

成员函数做友元是要注意表明是什么类下的成员函数(运用::)。

运算符重载

定义:对已有运算重新定义,赋予其另外一种功能。

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;
};
//通过全局函数重载加号
Person operator+(Person p1 ,Person p2)
{
	Person temp;
	temp.m_a = p1.m_a + p2.m_a;
	temp.m_b = p2.m_b + p2.m_a;
	return temp;
}


//实现Person p3 = p1+p2

int main()
{
	Person p1;
	p1.m_a = 10;
	p1.m_b = 20;
	Person p2;
	p2.m_a = 20;
	p2.m_b = 30;
	Person p3 = p1+p2;//成员函数方式 Person p3 = p1.operator+(p2);
	Person p4 = p1+p2;//全局变量方式 Person p4 = operator+(p1, p2);
	cout << p3.m_a << endl;
	cout << p4.m_a << endl;
	return 0;
}

2.左移运算符重载(<<)

主要是cout函数。

3.其余的运算符重载写法类似加号运算符重载,可以依葫芦画瓢。

继承

继承是面向对象三大特性之一

1.好处:减少重复代码

2.语法:class 子类 : 继承方式 父类

子类也被称为派生类,包括基类继承过来和自己特有的成员

父类也被称为基类

3.继承方式有三种:公共继承 保护继承 私有继承

父类中的私有内容无论是哪个继承方式都无法访问,而三种继承的区别是,公共继承不会改变从父类中继承过来的成员变量的属性(public仍然是public,protected仍然是protected),而保护继承公共权限和保护权限都变为保护权限,私有继承公共权限和保护权限都变为私有权限。

在这里插入图片描述

4.继承中的对象模型

class Base
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class Son :public Base
{
public:
	int m_d;
};

void test1()
{
	//16
	//父类中所有静态成员属性都会被子类继承下来,包括私有成员属性
	//父类中私有成员属性,被编译器隐藏,访问不到,但是确实被继承
	cout << "size of Son = " << sizeof(Son) << endl;
}

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

5.继承中的构造和析构顺序

先调用父类构造,再调用子类构造,析构顺序与构造顺序相反。

6.继承同名成员处理方式

a.调用自己的,直接 对象.成员名

b.调用父类,对象.作用域(即父类class名称)::成员名

c.如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类所有同名成员函数,如果要访问父类的成员,需要加作用域。

7.继承同名静态成员处理方式

与非静态同名成员类似。

a.访问子类,直接访问

b.访问父类,加作用域

c.同名静态成员处理和非静态处理方式一样,只不过有两种访问的方式(通过对象和通过类名)。

8.多继承语法

语法

class 子类: 继承方式 父类1,继承方式 父类2,...

多继承中当父类中出现同名成员,子类使用时要加作用域。

9.菱形继承

概念:两个son继承同一个father,grandson继承两个son。

问题:子类继承两份相同的数据,导致资源浪费。

解决办法:利用虚继承(可以上csdn搜索了解)可以解决菱形继承问题。

多态

好处:

a.组织结构清晰。

b.可读性强。

c.利于前后期的扩展和维护性高。

1.动态多态

满足条件:有继承关系,子类重写父类的虚函数。

使用:父类的指针或者引用指向子类对象。(Base *base = new Son)

(重写概念:函数返回值类型 函数名 参数列表完全一致称为重写)

2.纯虚函数和抽象类

a.纯虚函数语法

virtual 返回值类型 函数名 (参数列表) = 0;

当类中有了纯虚函数,这个类也被称为抽象类

b.抽象类特点:

无法实例化对象,子类必须重写抽象类的纯虚函数,否则也属于抽象类

在这里插入图片描述

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;
	int m_a;
	int m_b;
};

class Son:public Base
{
public:

};

void test1()
{
	Son s;//会报错,报错如上面的图片
	s.func();

}

若想不报错,则需重写纯虚函数

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;
	int m_a;
	int m_b;
};

class Son:public Base
{
public:
	virtual void func() {};//重写纯虚函数
};

void test1()
{
	Son s;
	s.func();//不会报错
}

3.虚析构和纯虚析构

问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码。

解决:将父类中的析构函数改为虚析构和纯虚析构。

共性:

a.解决父类指针释放子类对象。

b.都需要具体函数实现。

区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象。

虚析构语法

virtual ~类名(){}

纯虚析构语法

virtual ~类名() = 0;//类内声明
类名::类名(){}//类外实现

重写抽象类的纯虚函数,否则也属于抽象类

[外链图片转存中…(img-nZqPZLyV-1708327608132)]

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;
	int m_a;
	int m_b;
};

class Son:public Base
{
public:

};

void test1()
{
	Son s;//会报错,报错如上面的图片
	s.func();

}

若想不报错,则需重写纯虚函数

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;
	int m_a;
	int m_b;
};

class Son:public Base
{
public:
	virtual void func() {};//重写纯虚函数
};

void test1()
{
	Son s;
	s.func();//不会报错
}

3.虚析构和纯虚析构

问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码。

解决:将父类中的析构函数改为虚析构和纯虚析构。

共性:

a.解决父类指针释放子类对象。

b.都需要具体函数实现。

区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象。

虚析构语法

virtual ~类名(){}

纯虚析构语法

virtual ~类名() = 0;//类内声明
类名::类名(){}//类外实现

如果子类中没有堆区数据,可以不写虚析构和纯虚析构。

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值