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

news/2024/9/29 20:03:02/

原文链接: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 yarreturn 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 = "张三");  //两个构造函数 intPerson(double,string);   //两个构造函数 doublevoid 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 * pp = 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成员变成publicusing Base::dd;//将基类的protected成员变成public
private:using Base::show;//将基类的public成员变成privatestring 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::food.Base::foo();            // 调用Base::food.some_member = 10;       // 调用Derived::some_memberd.Base::some_member = 20; // 调用Base::some_memberreturn 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 showA *pA = &b;pA->show();//B show 如果show方法前没用virtual声明为虚函数,这里会输出A showsystem("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;//6return 0;
}

http://www.ppmy.cn/news/1532068.html

相关文章

Qt中多语言的操作(以QtCreator为例)

1、首先&#xff0c;我们在代码中与文本相关的且需要支持多语言的地方&#xff0c;用tr来包含多语言key&#xff08;多语言key是我们自己定义的&#xff09;&#xff0c;如下 //举例 QPushButton* btnnew QPushButton(this); btn->move(20,20); btn->resize(100,50); //…

配置STM32F103的高级定时器TIM1用于PWM功能

配置STM32F103的高级定时器TIM1用于PWM功能 之前在使用stm32f103的PA9引脚复用为高级定时器TIM1_CH2&#xff0c;用它来输出PWM波时发现无法正常输出PWM波形。出现这种问题的情况一般是将PA9先初始化成了串口&#xff0c;然后又配置成PWM功能&#xff0c;这样会导致无法输出PW…

Github 2024-09-23 开源项目周报 Top15

根据Github Trendings的统计,本周(2024-09-23统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6C++项目3C项目3HTML项目2PowerShell项目1TypeScript项目1JavaScript项目1Blade项目1PHP项目1Bootstrap 5: Web上开发响应式、移动优…

百度智能体创建:情感领域的创新力量

《百度智能体&#xff1a;情感领域的创新力量》 一、百度智能体&#xff1a;开启智能新征程 百度智能体&#xff0c;准确叫法为百度文心智能体。它是由百度推出的基于文心大模型的智能体平台。2023 年 9 月 1 日&#xff0c;平台的前身 “灵境矩阵” 上线并开始内测&#xff0c…

高并发内存池(六):补充内容

目录 有关大于256KB内存的申请和释放处理方法 处理大于256KB的内存申请 补充内容1 补充内容2 补充内容3 处理大于256KB的内存释放 新增内容1 新增内容2 测试函数 使用定长内存池替代new 释放对象时不传对象大小 补充内容1 补充内容2 补充内容3 补充内容4 测试…

如何使用ssm实现个人日常事务管理系统+vue

TOC ssm718个人日常事务管理系统vue 研究背景 自计算机发展以来给人们的生活带来了改变。第一代计算机为1946年美国设计&#xff0c;最开始用于复杂的科学计算&#xff0c;占地面积、开机时间要求都非常高&#xff0c;经过数十几的改变计算机技术才发展到今天。现如今已是电…

服务保护sentinel

线程隔离 - 线程池隔离&#xff1a;给每个服务调用业务分配一个线程池&#xff0c;利用线程池本身实现隔离效果。 - 信号量隔离&#xff1a;不创建线程池&#xff0c;而是计数器模式&#xff0c;记录业务使用的线程数量&#xff0c;达到信号量上限时&#xff0c;禁止新的请求。…

在Android开发中可以用到的Ui控件有哪些?

目录 1. 文本控件 2. 按钮控件 3. 选择控件 4. 布局控件 5. 图像控件 6. 列表控件 7. 对话框和提示 8. 菜单控件 9. 工具栏和导航控件 10. 进度控件 11. 时间与日期控件 12. 其他控件 13. 高级控件 14. 自定义控件 15. 其他 总结&#xff1a; 在 Android 开发中…