一文说完c++全部基础知识,类,多态,继承,虚函数,友元,构造,重载(一)

原文链接:https://blog.csdn.net/qq_33670157/article/details/104455787

原文写的不够好,我已经进行总结优化。要看懂本文,需要有C语言,c++基础。一文有点长,打算分多篇来解释
分为c++基础,c++文件流相关的操作,网络编程。

一、C++和C
C语言是结构化和模块化的语言,面向过程。
C++保留了C语言原有的所有优点,增加了面向对象的机制,俗称“带类的C",1983年更名为C++

在这里插入图片描述

在这里插入图片描述

运算符

算术运算符:		+ - * / % ++ --
关系运算符:		== != < > >= <=
逻辑运算符:		&& || !
位运算符:		& | ^ ~ << >>
赋值运算符:		= += -= *= /= %= <<= >>= &= ^= !=

在这里插入图片描述

一、面向对象,类
类也是一种数据类型。

类的声明:

class 类名
{
	public:
		公有数据成员;
		公有成员函数;
	private:
		私有数据成员;
		私有成员函数;
	protected:
		保护数据成员;
		保护成员函数;
};

成员函数的定义:类内,类外,类外内联函数

//类外
返回类型 类名:成员函数名(参数列表)
{
	函数体;
}
//内联函数:类外
inline 返回类型 类名:成员函数名(参数列表)
{
	函数体;
}

内联函数的代码会直接嵌入到主调函数中,可以节省调用时间,如果成员函数在类内定义,自动为内联函数。

类成员的访问权限以及类的封装
和Java、C#不同的是,C++中public、private、protected只能修饰类的成员,不能修饰类,C++中的类没有共有私有之分
类内部没有访问权限的限制,都可以互相访问。
在C++中用class定义的类中,其成员的默认存取权限是private。
在这里插入图片描述

类(class)与结构体(struct)的区别
在这里插入图片描述

//结构体默认权限为public

struct person
{
	void show();
	string name;
	int age;
};

int main()
{
	person p;
	p.name = "heiren";
	p.age = 666;
	p.show();
	cout <<"name="<< p.name <<" age="<< p.age << endl;
	system("pause");
	return 0;
}

将struct改为class,运行报错。因为class不允许对象直接访问成员变量,只能通过成员函数访问。因为默认是private的。

二、对象

1.声明类同时定义对象

class 类名
{
	类体;
}对象名列表;

2.先声明类,再定义对象

class 类名
{
	类体;
}

类 对象

3. 不出现类名,直接定义对象

class 
{
	类体;
}对象名列表;

4.在堆上创建对象

Person p(123, "yar");//在栈上创建对象
Person *pp = new Person(234,"yar");//在堆上创建对象

注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行

对象成员的引用方式:
对象名.数据成员名
或者 对象名.成员函数名(参数列表)

三、构造函数

是一种特殊的成员函数,主要功能是为对象分配存储空间,以及为类成员变量赋初值

构造函数名必须与类名相同
没有任何返回值和返回类型
创建对象自动调用,不需要用户来调用,且只掉用一次
类没有定义任何构造函数,编译系统会自动为这个类生成一个默认的无参构造函数

构造函数定义
1.类中定义
2.类中声明,类外定义

[类名::]构造函数名(参数列表)
{
	函数体
}

1、带默认参数的构造函数

class Person
{
public:
	Person(int = 0,string = "张三");//类中声明构造函数,带默认参数初始化
	void show();
private:
	int age;
	string name;
};

Person::Person(int a, string s)//类外定义构造函数
{
	cout<<a<<" "<<s<<endl;
	age = a;
	name = s;
}

void Person::show()
{
	cout << "age="<<age << endl;
	cout << "name=" <<name << endl;
}

int main()
{
    Person p;                  //对象输出为,0 张三
	Person p2(12);             //对象输出为 ,12 张三
	Person p3(123, "yar");     //对象输出为 ,123 yar
	return 0;
}

2、带参数初始化表的构造函数

类名::构造函数名(参数列表):参数初始化表
{
	函数体;
}

参数初始化列表的一般形式:
参数名1(初值1),参数名2(初值2),…,参数名n(初值n)

class Person
{
public:
	Person(int = 0,string = "张三");
	void show();
private:
	int age;
	string name;
};

Person::Person(int a, string s):age(a),name(s)//类外定义构造函数,初始化列表
{
	cout << a << " " << s << endl;
}

构造函数重载:
构造函数名字相同,参数个数和参数类型不一样。

class Person
{
public:
	Person();
	Person(int = 0,string = "张三");  //两个构造函数 int
	Person(double,string);   //两个构造函数 double
	void show();
private:
	int age;
	double height;
	string name;
};

拷贝构造函数

类名::类名(类名&对象名)
{
	函数体;
}
class Person
{
public:
	Person(Person &p);//声明拷贝构造函数
	Person(int = 0,string = "张三");
	void show();
private:
	int age;
	string name;
};

Person::Person(Person &p)//定义拷贝构造函数
{
	cout << "拷贝构造函数" << endl;
	age = 0;
	name = "ABC";
}

Person::Person(int a, string s):age(a),name(s)
{
	cout << a << " " << s << endl;
}

int main()
{
	Person p(123, "yar");
	Person p2(p);
	p2.show();
	return 0;
}

// p输出结果
123 yar

//拷贝构造函数输出结果
age=0
name=ABC

五、析构函数
在这里插入图片描述

析构函数的定义:
1.类中定义
2.类中声明,类外定义

[类名::]~析构函数名()
{
	函数体;
}

六、指针

1、指向对象的指针

对象指针的声明和使用

类名 *对象指针名;
对象指针 = &对象名;

访问对象成员方法

对象指针->数据成员名
对象指针->成员函数名(参数列表)

Person p1(123, "yar");
Person * Ptr1 = &p1;

Person * Ptr2 = new Person(234,"yar")


Ptr2 ->show();

2、指向对象成员的指针

数据成员类型 *指针变量名 = &对象名.数据成员名;
函数类型 (类名::*指针变量名)(参数列表);
指针变量名=&类名::成员函数名;
(对象名.*指针变量名)(参数列表);


Person p(123, "yar");
void(Person::*pfun)();

pfun = &Person::show;

(p.*pfun)();

解释
void show()是个void返回类型,返回值是void
void(Person:: * pfun)()是什么意思呢,拆开来分析
C语言里定义指针时是这样 int * ptr,ptr是int型的指针,C语言里定义函数指针时是(int * pfunc)()。pfunc指向一个函数,这个指针时int型。
所以void(Person::*pfun)()意思,就死pfun是个指针,指向这个函数,这个指针类型是person类,类型,这个函数返回值是void型。

(p.*pfun)()的意思,先解析解引用pfun,是show,(p.*pfun)()等同于Ptr2 ->show()

八、this指针
每个成员函数都有一个特殊的指针this,它始终指向当前被调用的成员函数操作的对象

其实类似于C语言的结构体,头指针,这个头,就是一段内存地址的首地址,有了头,就可以访问到所有地址。

struct person
{
	struct person *ptr;
	int a;
	void show();
}

struct person a;

struct person * p

p = a;//a的地址给p,就是把这个结构体的首地址,指向了这个结构体,this就是这个意思

p->show();

p = a.ptr; //this就是这个意思
class Person
{
public:
	Person(int = 0,string = "张三");
	void show();
private:
	int age;
	string name;
};

Person::Person(int a, string s):age(a),name(s)
{
	cout << a << " " << s << endl;
}

void Person::show()
{
	cout << "age="<<this->age << endl;
	cout << "name=" <<this->name << endl;
}

九、友元
借助友元(friend),可以使得其他类中得成员函数以及全局范围内得函数访问当前类得private成员。

我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
其实发现偶尔需要这样做,所以强行打补丁,找借口

1、友元函数

友元函数不是类的成员函数,所以没有this指针,必须通过参数传递对象。
友元函数中不能直接引用对象成员的名字,只能通过形参传递进来的对象或对象指针来引用该对象的成员。

1.将非成员函数声明为友元函数

class Person
{
public:
	Person(int = 0,string = "张三");
	friend void show(Person *pper);//将show声明为友元函数,外部一个普通成员函数
private:
	int age;
	string name;
};

Person::Person(int a, string s):age(a),name(s)
{
	cout << a << " " << s << endl;
}

void show(Person *pper)//非类。就是外部一个普通成员函数
{
	cout << "age="<< pper->age << endl;
	cout << "name=" << pper->name << endl;
}

int main()
{;
	Person *pp = new Person(234,"yar");
	show(pp);
	system("pause");
	return 0;
}

2.将其他类的成员函数声明为友元函数

person中的成员函数可以访问MobilePhone中的私有成员变量

class MobilePhone;//提前声明

//声明Person类
class Person
{
public:
	Person(int = 0,string = "张三");
	void show(MobilePhone *mp);
private:
	int age;
	string name;
};

Person::Person(int a, string s):age(a),name(s)//构造函数
{
	cout << a << " " << s << endl;
}

void Person::show(MobilePhone *mp)//内部的类成员函数,访问到了,其他类的成员数据。
{
	cout << mp->year << "年  " << mp->memory << "G " << mp->name << endl;
}



//声明MobilePhone类
class MobilePhone
{
public:
	MobilePhone();
	friend void Person::show(MobilePhone *mp);//将外部类成员函数,声明为友员,可以这样,他假装是他的内部成员函数,就可以访问成员了。
private:
	int year;
	int memory;
	string name;
};

MobilePhone::MobilePhone()
{
	year = 1;
	memory = 4;
	name = "iphone 6s";
}


int main()
{
	Person *pp = new Person(234,"yar");
	MobilePhone *mp = new MobilePhone;
	pp->show(mp);//传一个对象指针,就可以访问了
	system("pause");
	return 0;
}

友元类

语法形式:friend [class] 友元类名

当一个类为另一个类的友元时,称这个类为友元类。 友元类的所有成员函数都是另一个类中的友元成员。

在这里插入图片描述

我的理解,就是C语言里的结构体套结构体封装,就可以访问任何数据了。

class HardDisk
{
public:
	HardDisk();
	friend class Computer;
private:
	int capacity;
	int speed;
	string brand;
};

HardDisk::HardDisk():capacity(128),speed(0),brand("三星")
{
}

class Computer
{
public:
	Computer(HardDisk hd);
	void start();
private:
	string userName;
	string name;
	int ram;
	string cpu;
	int osType;
	HardDisk hardDisk;
	
};

Computer::Computer(HardDisk hd):userName("yar"),name("YAR-PC"),ram(16),cpu("i7-4710"),osType(64)
{
	cout << "正在创建computer..." << endl;
	this->hardDisk = hd;
	this->hardDisk.speed = 5400;
	cout << "硬盘转动...speed = " << this->hardDisk.speed << "转/分钟" << endl;
	
}

void Computer::start()
{
	
	cout << hardDisk.brand << " " << hardDisk.capacity << "G" << hardDisk.speed << "转/分钟" << endl;
	cout << "笔记本开始运行..." << endl;
}

int main()
{
	HardDisk hd;
	Computer cp(hd);
	cp.start();
	system("pause");
	return 0;
}

十、继承和派生

1 、继承和派生概述
继承就是再一个已有类的基础上建立一个新类,已有的类称基类或父类,新建立的类称为派生类和子类;派生和继承是一个概念,角度不同而已,继承是儿子继承父亲的产业,派生是父亲把产业传承给儿子。

一个基类可以派生出多个派生类,一个派生类可以继承多个基类
这句话感觉很拗口,其实不然,比如举个例子

1、一个派生类可以继承多个基类,的理解例子

定义一个吃的行为类
吃
{
	躺着吃,
	站在吃,
	坐着吃
}

定义一个走路的行为类
{
	两只脚走路,
	四只脚走路,
}

定义一个睡觉的行为类
{
	平躺着睡,
	侧躺着睡,
	趴躺着睡
}

然后就可以多继承了,继承了上面所有的行为,就是上面说的,一个派生类可以继承多个基类
定义一个动物狗类
{
	吃的行为类
	走路的行为类
	睡觉的行为类
}

2、一个基类可以派生出多个派生类,的理解例子

定义一个吃的行为类
吃
{
	躺着吃,
	站在吃,
	坐着吃
}

定义一个走路的行为类
{
	两只脚走路,
	四只脚走路,
}

定义一个睡觉的行为类
{
	平躺着睡,
	侧躺着睡,
	趴躺着睡
}


然后一个基类就可以派生很多类了,如下面的猪狗,羊,牛,都有吃的行为,都是吃派生出来的。

定义一个动物狗类
{
	吃的行为类
	走路的行为类
	睡觉的行为类
}

定义一个动物牛类
{
	吃的行为类
	走路的行为类
	睡觉的行为类
}

定义一个动物羊类
{
	吃的行为类
	走路的行为类
	睡觉的行为类
}

派生类的声明:

class 派生类名:[继承方式] 基类名
{
	派生类新增加的成员声明;
};

继承方式为可选项,默认为private,还有public,protected

在这里插入图片描述
总结,如果是受保护不可见的,什么方式继承,都不可随意访问(受保护或不可见),如果是公开的,只有公开继承可见,其他方式继承都是原来是怎么样就怎么样。

利用using关键字可以改变基类成员再派生类中的访问权限;using只能修改基类中public和protected成员的访问权限。

class Base
{
public:
	void show();
protected:
	int aa;
	double dd;
};

void Base::show(){
}

class Person:public Base
{
public:
	using Base::aa;//将基类的protected成员变成public
	using Base::dd;//将基类的protected成员变成public
private:
	using Base::show;//将基类的public成员变成private
	string name;
};

int main()
{
	Person *p = new Person();
	p->aa = 12;
	p->dd = 12.3;
	p->show();//出错
	delete p;
	return 0;
}

在C++中,如果基类和派生类中含有同名的成员

在C++中,如果基类和派生类中含有同名的成员,那么在派生类中直接访问该成员时将优先使用派生类自己的成员,这被称为"成员屏蔽"(member shadowing)。如果需要访问基类中被屏蔽的同名成员,则必须使用基类的作用域解析运算符(::)
class Base {
public:
    void foo() {
        // 基类的foo函数
    }
    int some_member;
};
 
class Derived : public Base {
public:
    void foo() {
        // 派生类的foo函数
    }
    int some_member;
};
 
int main() {
    Derived d;
    d.foo();                  // 调用Derived::foo
    d.Base::foo();            // 调用Base::foo
    d.some_member = 10;       // 调用Derived::some_member
    d.Base::some_member = 20; // 调用Base::some_member
    return 0;
}

派生类的构造函数和析构函数
在这里插入图片描述

class Base
{
public:
	Base(int, double);
	~Base();
private:
	int aa;
	double dd;
};

Base::Base(int a, double d) :aa(a), dd(d)
{
	cout << "Base Class 构造函数!!!" << endl;
}

Base::~Base()
{
	cout << "Base Class 析构函数!!!" << endl;
}

class Person:public Base
{
public:
	Person(int,double,string);
	~Person();
private:
	string name;
};

Person::Person(int a,double d,string str):Base(a,d),name(str)// 构造函数定义,初始化
{
	cout << "Person Class 构造函数!!!" << endl;
}

Person::~Person()
{
	cout << "Person Class 析构函数!!!" << endl;
}

int main()
{
	cout << "创建Person对象..." << endl;
	Person *p = new Person(1,2,"yar");
	cout << "删除Person对象...." << endl;
	delete p;
	system("pause");
	return 0;
}

在这里插入图片描述

多继承
一个派生类同时继承多个基类的行为。

多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承

多重继承派生类声明的一般形式:

class 派生类名:继承方式1 基类1,继承方式2 基类2
{
	派生类主体;
};

多重继承派生类的构造函数:

派生类名(总参数列表):基类名1(基类参数列表1),基类名2(基类参数列表2),

子对象名1,...(参数列表)
{
	构造函数体;
}`

二义性问题:多个基类中有同名成员,出现访问不唯一的问题。

十一、虚基类
c++引入虚基类使得派生类再继承间接共同基类时只保留一份同名成员。

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class)。

怎么理解呢,如上面举例说的

假如定义一个动物类
{
	狗,
	牛
	猪
}

这些动物都继承了吃的行为,那动物类就继承了三次,这个类就有三个相同的吃的函数,显然是不对的。
虚基类的声明:class 派生类名:virtual 继承方式 基类名

class  A//虚基类
{
	protected:
	int a;
};

class B: virtual public A
{
	protected:
	int b;
};

class C:virtual public A
{
protected:
	int c;
};

class D:public B,public C
{
protected:
	int d;
	void show()
	{
		b = 123;
		c = 23;
		a = 1;
	}
};

派生类的 同名成员 比虚基类的 优先级更高
在这里插入图片描述

十二、多态和虚函数

多态

不同的对象可以使用同一个函数名调用不同内容的函数。
在这里插入图片描述

虚函数
实现程序多态性的一个重要手段,使用基类对象指针访问派生类对象的同名函数。
这玩意有点像C语言的函数指针,回掉函数
在这里插入图片描述

class  A
{
public:
	virtual void show()
	{
		cout << "A show" << endl;
	}
};

class B:  public A
{
public:
	void show()
	{
		cout << "B show" << endl;
	}
};

int main()
{
	
	B b;
	b.show();//B show
	A *pA = &b;
	pA->show();//B show 如果show方法前没用virtual声明为虚函数,这里会输出A show
	
	system("pause");
	return 0;
}

纯虚函数
在基类中不执行具体的操作,只为派生类提供统一结构的虚函数,将其声明为虚函数。

class  A
{
public:
	virtual void show() = 0;
};

class B:  public A
{
public:
	void show()
	{
		cout << "B show" << endl;
	}
};

抽象类:包含纯虚函数的类称为抽象类。由于纯虚函数不能被调用,所以不能利用抽象类创建对象,又称抽象基类。

十三、运算符重载

所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

函数类型 operator运算符(参数列表)
{
	函数体
}
//举个栗子:定义一个向量类,通过运算符重载,可以用+进行运算。
class Vector3
{
public:
	Vector3();
	Vector3(double x,double y,double z);
public:
	Vector3 operator+(const Vector3 &A)const;
	void display()const;
private:
	double m_x;
	double m_y;
	double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//运算符重载
Vector3 Vector3::operator+(const Vector3 &A) const
{
	Vector3 B;
	B.m_x = this->m_x + A.m_x;
	B.m_y = this->m_y + A.m_y;
	B.m_z = this->m_z + A.m_z;
	return B;
}
void  Vector3::display()const
{
	cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}

在这里插入图片描述

class Vector3
{
public:
	Vector3();
	Vector3(double x,double y,double z);
public:
	Vector3 operator+(const Vector3 &A)const;
	Vector3 operator++();
	friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
	friend Vector3 operator--(Vector3 &v);
	void display()const;
private:
	double m_x;
	double m_y;
	double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//运算符重载
Vector3 Vector3::operator+(const Vector3 &A) const
{
	Vector3 B;
	B.m_x = this->m_x + A.m_x;
	B.m_y = this->m_y + A.m_y;
	B.m_z = this->m_z + A.m_z;
	return B;
}
Vector3 Vector3::operator++()
{
	this->m_x ++;
	this->m_y ++;
	this->m_z ++;
	return *this;
}
void  Vector3::display()const
{
	cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}
Vector3 operator-(const Vector3 &v1,const Vector3 &v2)
{
	Vector3 B(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z);
	return B;
}
Vector3 operator--( Vector3 &v)
{
	v.m_x--;
	v.m_y--;
	v.m_z --;
	return v;
}
int main()
{
	Vector3 v1(1, 2, 3);
	Vector3 v2(2, 3, 2);
	
	++v1;//v1.operator++(); 作为类成员函数可以显式调用
	v1.display();
	--v2;
	v2.display();
	Vector3 v3 = v1 + v2;// v1.operator+(v2);作为类成员函数可以显式调用
	v3.display();
	Vector3 v4 = v1 - v2;
	v4.display();
	return 0;
}

在这里插入图片描述

在这里插入图片描述

输入\输出运算符重载

friend ostream &operator<<( ostream &output, const Vector3 &v )
{ 
   output << "F : " <<v.m_x<< " I : " << v.m_y<<v.m_z;
   return output;            
}

friend istream &operator>>( istream  &input, Vector3 &v )
{ 
   input >> v.m_x>> v.m_y>>v.m_z;
   return input;            
}

在这里插入图片描述

operator 类型名()
{
	转换语句;
}
class Vector3
{
public:
	Vector3();
	Vector3(double x,double y,double z);
public:
	Vector3 operator+(const Vector3 &A)const;
	Vector3 operator++();
	friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
	friend Vector3 operator--(Vector3 &v,int);
	operator double()
	{
		return m_x + m_y + m_z;
	}
	void display()const;
private:
	double m_x;
	double m_y;
	double m_z;
};
int main()
{
	Vector3 v1(1, 2, 3);
	double d = v1;
	cout << d << endl;//6
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值