14、c++的对象
对象和结构的区别;
结构:包含各种类型的变量;
对象:包含各种函数、和变量;
设计对象的时候引用class关键字创建类,和结构形状差不多;
将变量称之为属性,函数称之为方法;
14造车,创建类:类的实际应用;
一个awm型号的车;
属性(c变量):车身颜色、油
方法(c函数):改变颜色、加油;
类的声明和定于;
#include <iostream>class MyClass_che
{
public:MyClass_che();~MyClass_che();std::string yanse;void gaibian_yanse(std::string);int you;void gaibian_you(int);private:};
void MyClass_che::gaibian_yanse(std::string str)// ::表域符 ,指示实在这个类里面的方法/或属性
{MyClass_che::yanse = str;
}
void MyClass_che::gaibian_you(int you)
{MyClass_che::you = you;
}
类定义时候:不能对常量赋值,除非是加static
awm a;创建了一个对象a属于awm型号的车
以上结合就理解了这就是对象;这个对象有颜色、油属性;有改变颜色、改变油的方法;
作用域 ::表域符 ,指示是在这个类里面的方法/或属性
同类对象可以赋值;
awm a,b;
b=a;
16构造器
构造器和通常方法的主要区别;
-构造器的名字必须和它所在的类的名字一样;
-系统在创建某个类的实例时会第一时间自动调用这个类的构造器;
-构造器永远不会返回任何值
构造器如下
构造器定义如下;相当于一个函数,没有viod,且在声明创建一个对象后被执行;(相当于c里面的 回调函数,创建一个对象就回调)
MyClass_che::MyClass_che()
{
}
每个类至少一个构造器;没写构造器,编译器也会给你定义一个构造器( 内容为空 );
析构器
属性和构造器一样;不过前面加~波浪线
程序结束时调用:该程序使用内存,就在析构器里面释放内存
应用
比如构造器打开文本:打开文本需要内存
,析构器关闭文本 ; 关闭文本就释放了内存
#include <iostream>
#include<fstream>
class MyClass_che
{
public:MyClass_che();~MyClass_che();std::string yanse;void gaibian_yanse(std::string);std::fstream pf;int you;void gaibian_you(int);private:};MyClass_che::MyClass_che()
{pf.open("123.txt", std::ios::in | std::ios::out);
}MyClass_che::~MyClass_che()
{pf.close();
}
17this的继承和类的继承
this指针,表当前方法的所属类;
void MyClass_che::gaibian_you(int you)
{MyClass_che::you = you;this->you = you;//此时的this等效于MyClass_che,右边的you代表形参,左边代表这个类的属性
}
基类:
子类;
两个类的描述:
基类是公共有的,子类继承基类,并且多于基类的属性和方法;
例如;
动物是基类
{
吃饭、睡觉
}
甲鱼子类、猪是子类
甲鱼
{
基类的属性和方法
独特属于甲鱼的属性:游泳
}
猪
{
基类的属性和方法
独特属于猪的属性:上树
};
#include <iostream>
class dongwu//基类
{
public:dongwu();~dongwu();void eat();private:};void dongwu::eat()//基类方法
{std::cout << "我在吃饭" << std::endl;
}
dongwu::dongwu()
{
}dongwu::~dongwu()
{
}
class pig:public dongwu//pig 继承于(分号) dongwu
{
public:pig();~pig();void shangshu();//属于猪的独特属性private:};pig::pig()
{
}pig::~pig()
{
}
void pig::shangshu()
{std::cout << "上树了" << std::endl;
}int main()
{pig pig1;//构建一只猪1对象,类别是子类pigpig1.eat();//这是继承的基类的方法pig1.shangshu();//这是子类特有的std::cout << "Hello World!\n";
}
18子类的构造器和析构
创建对象时:
先执行父类的构造器
才到子类的构造器;
销毁对象时:
子类的最后一条语句结束后调用基类的析构器;
- 首先调用子类的析构器。
- 然后调用父类的析构器。
带参数的构造器声明
父类,基类
class dongwu
{
public:dongwu( std::string N_name);//带参的基类析构函数~dongwu();private:};
子类
class pig:public dongwu//继承父类的子类
{
public:pig(std::string N_name);//带参的子类析构函数~pig();private:};
构造器定义
带参子类构造器的定义
//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
dongwu::dongwu(std::string N_name)
{
name = N_name;
}
这个name = N_name;
个人认为是:相当于tihs->name = N_name; 初始化了tihs->name;
然后再继承给子类
pig::pig(std::string N_name):dongwu::dongwu( N_name)//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
{
}
子类析构器
编译器自动添加;可不用处理;
19访问控制
类的属性和方法有访问控制
访问控制可以在编译的时候编译器可以检查到合法性;
在声明类的时候,访问控制如下关键字
public: 公共 此下的属性和方法 可以被任何代码访问
protected: 保护 此下的属性和方法,可以被这个类本身方法和子类方法访问
1、外部代码无法通过过子类去调用父类的保护
private: 私有 此下的属性和方法,只有这个类本身可以访问
class dongwu
{
public://任何代码都可以访问dongwu( std::string N_name);//带参的基类析构函数~dongwu();std::string name;protected://子类方法内和这个类方法内可以访问int a;
private://只能 dongwu temp;由这个类的 temp 访问int b;};
main函数里面无法找到a和b;a被保护 b是私有;
在pig类方法里面,可以引用父类的被保护的a;
pig::pig(std::string N_name):dongwu::dongwu( N_name)//子构造器的形式 子构造器 :父类构造器(不带类型 的参数)
{//name = N_name;this->a = 0;
}
20覆盖函数和重载
覆盖如下:
子类继承了父类的eat()方法,
子类中又声明且定义了eat()的方法,此时子类调用的eat方法,就会覆盖继承父类的eat();
所以子类调用的时候,调用的方法其实就是子类的eat();
class dongwu
{
public://任何代码都可以访问dongwu( std::string N_name);//带参的基类析构函数~dongwu();std::string name;void eat();protected://子类方法内和这个类方法内可以访问int a;
private://只能 dongwu temp;由这个类的 temp 访问int b;};void dongwu::eat()//父类的
{std::cout << " eat \n";
}
class pig:public dongwu//继承父类的子类
{
public:pig(std::string N_name);//带参的子类析构函数~pig();void eat();private:};void pig::eat()//子类的
{std::cout << "吃到了 大蛋糕";
}
重载
通过重载等效覆盖,通过改变子类的参数个数,或者参数类型,就可以保留继承父类的eat()
C++学习笔记-CSDN博客
参考其中的章节: 06:函数的重载
class dongwu
{
public://任何代码都可以访问dongwu( std::string N_name);//带参的基类析构函数~dongwu();std::string name;void eat();protected://子类方法内和这个类方法内可以访问int a;
private://只能 dongwu temp;由这个类的 temp 访问int b;};void dongwu::eat()//父类的
{std::cout << " eat \n";
}
class pig:public dongwu//继承父类的子类
{
public:pig(std::string N_name);//带参的子类析构函数~pig();void eat(int);private:};void pig::eat(int)//子类的
{dongwu::eat();//只要前面文本定义了这个 方法就可以直接这样调用std::cout << "吃到了 大蛋糕";
}
补充: dongwu::eat();//只要前面文本”定义 “了这个 方法就可以直接这样调用
21友元关系
在aa类声明的时候添加
friend class bb;//指定友元为bb,所以类bb可以访问aa的保护
#include <iostream>
class aa
{
public:
protected:int k1;
private:friend class bb;//指定友元为bb,所以类bb可以访问aa的保护
};class bb
{
public:void set(class aa* k);
protected:int y1;
private:
};void bb::set(class aa * k)//传递aa的类指针
{k->k1 = 0;//调用了aa的保护属性,无错误,所以可以调用
}int main()
{std::cout << "Hello World!\n";
}
22、静态属性和静态方法
静态成员
1、需要在外部定义和初始化
2、只需要在类里面声明;
3、静态成员先于类,不是独特地属于任何类;
静态成员是所有同类对象共享的,再静态方法里面不能访问普通成员;
普通方法可以访问静态成员和方法;
this指针
1、this指针每个类自动生成的私有成员
2、当一个对象被生成的时候,该对象的this指针就指向了对象的首地址
3、任何方法都可以用this指针,这个this指针是隐藏传送到方法里面的;get()其实是get(类指针* this );方法被调用指针就传入;
4、this可以指向每个成员和方法
结论:所以静态方法不属于任何对象,所以无法确定为对象地址,所以无法使用this指针;
调用方法坚持使用 类::方法;可以提高代码阅读性
如cat::get();
静态成员共享例子
#include <iostream>
#include <string>using namespace std;
class dongwu
{
public:dongwu(std::string N_name);~dongwu();static int get_cout();int get();std::string name;private:static int cout;int a;};
int dongwu::cout = 0;
int dongwu::get_cout()
{
// a++; 会编译错误return cout;
}
int dongwu::get()
{return cout;
}
dongwu::dongwu(std::string N_name)
{name = N_name;cout++;std::cout << name<<cout;
}dongwu::~dongwu()
{cout--;
}
class cat:public dongwu
{
public:cat(std::string N_name);
};
cat::cat(std::string N_name):dongwu::dongwu(N_name)
{
}int main()
{cat a1("小猫");cat a2("小猫");std::cout << '\n' << a1.get()<<'\n';{cat a3("小猫");cat a4("小猫");std::cout << '\n' << a1.get_cout() << '\n';}std::cout << '\n' << a1.get_cout() << '\n';
}
24虚方法;
虚方法关键字 在类的声明里面的方法添加关键字virtual
class dongwu
{
public:dongwu(std::string N_name);~dongwu();std::string name;virtual void play();///虚方法
private:static int cout;};
应用场景:
//指针是基类,创建的内存是子类;导致调用子类方法不能覆盖,必须用虚方法解决
#include <iostream>
#include <string>using namespace std;
class dongwu
{
public:dongwu(std::string N_name);~dongwu();std::string name;virtual void play();///虚方法
private:static int cout;};
dongwu::dongwu(std::string N_name)
{name = N_name;
}
void dongwu::play()
{std::cout << "动物在玩";
}dongwu::~dongwu()
{
}
class cat:public dongwu
{
public:cat(std::string N_name);void play();};
cat::cat(std::string N_name) :dongwu(N_name)
{}
void cat::play()//子类方法
{std::string str=this->name;str.append("这只猫再玩毛线");std::cout << str;
}int main()
{dongwu* p = new cat("小花");//指针是基类,创建的内存是子类;导致调用子类方法不能覆盖,必须用虚方法解决p->play();//如果基类不加virtual 虚方法关键字 ,方法是基类方法,子类没有成功覆盖}
补充:
new 创建内存
delete 删除内存
new cat ;创建了一个子类内存
delete :释放了一个子类的内存
25、纯虚方法
纯虚方法:
在虚方法后面=0;
作用,优化代码,告诉编译器,不需要寻找执行实现,这是一个纯虚方法,任何都可以覆盖,也可以说不用可以去覆盖了,每个继承的都可以无顾虑的实现覆盖;
多态性:
一个名字,可以执行不同的操作;
编译时的多态:通过重载实现,编译时运行快
运行时的多态:通过虚函数实现,运行时灵活和抽象
析构函数就是虚方法;
是为了当基类指针删除子类对象时,正确调用子类析构函数;
思路(仅仅为了理解):相当于为了覆盖子类方法,用虚方法,使得调用方法时是执行子类方法,而不是基类的方法;
特别说明:基类被继承后,基类的析构才是虚函数;
26、运算符重载
以下5个运算符不允许重载
. (成员访问运算符)
.* 成员指针访问运算符(本人认为 ->)
::域运算符
sizeof( ) (尺寸运算符)
?: (条件运算符)
运算符重载形式:
函数类型 operator 运算符(参数列表);
Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类
当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下(函数运算)
例子如下备注,仅个人理解
#include <iostream>
class Complex
{
public:Complex();~Complex();Complex(double r, double i);Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算void print();private:double real;double imag;
};Complex::Complex()
{
}Complex::~Complex()
{
}Complex::Complex(double r, double i)//重载函数
{real = r;imag = i;
}
Complex Complex::operator +(Complex& d)//括号里面的是类对象,传入对象 运算符重载
{Complex c;c.imag = imag + d.imag;c.real = real + d.real;return c;
}void Complex::print()
{std::cout << "(" << real << "," << imag << "i)";
}int main()
{Complex c1(1.0, 2.0), c2(3.0, -4.0), c3;c3 = c1 + c2;//该对象是Complex类遇到+,发生了重载,使用指定的运算int a, b=8, c=9;a = b + c;c3.print();std::cout << "Hello World!\n"<<a;
}
小鱼理解:
c1+c2
c1.operator+(c2);编译器理解为,相当于调用一个函数得到返回值
运算符重载作为类友元函数
运算符重载
Complex operator +(Complex &a, Complex &b)//在类中被声明了友元,所以可以访问私有;这同时是个运算符重载
{return Complex(a.real + b.real, a.imag + b.imag);//
}
在类里面声明为友元
class Complex
{
public:Complex();~Complex();Complex(double r, double i);// Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算friend Complex operator +(Complex& a, Complex& b);//声明为该类的友元函数void print();private:double real;double imag;
};
例子
#include <iostream>
class Complex
{
public:Complex();~Complex();Complex(double r, double i);// Complex operator +(Complex&d);//重载运算符 相当于返回值类型为Complex类 当 Complex类 遇到加号 且后面为同类Complex,运算符执行如下运算friend Complex operator +(Complex& a, Complex& b);//声明为该类的友元函数void print();private:double real;double imag;
};Complex::Complex()
{
}Complex::~Complex()
{
}
Complex::Complex(double r, double i)
{real = r;imag = i;
}
Complex operator +(Complex &a, Complex &b)//在类中被声明了友元,所以可以访问私有;这同时是个运算符重载
{return Complex(a.real + b.real, a.imag + b.imag);//
}void Complex::print()
{std::cout << "(" << real << "," << imag << "i)";
}int main()
{Complex c1(1.0, 2.0), c2(3.0, -4.0), c3;c3 = c1 + c2;//该对象是Complex类遇到+,发生了重载,使用指定的运算int a, b=8, c=9;a = b + c;c3.print();std::cout << "Hello World!\n"<<a;
}
28再次重载<< (对数据流)
原理和26差不多,且用得不多;
29、多继承
当一个对象有多个身份的时候,就有了多种关系
一个人既可以是学生也可以是老师;基类是人,子类是老师、学生;还有一个子类既是学生也是老师;
因为多继承:所以可以访问不同父类的保护成员;
多继承的类声明
class 子类: public 父类, public 父类 //(class 子类:分号 public 父类, 逗号public 父类)
多继承的析构器
子类::子类析构(std::string N_name) : 分号 父类析构( N_name), 逗号 父类析构(N_name)
{
}
N_name为参数;
techer_student::techer_student(std::string N_name):techter( N_name), student(N_name)
{
}
例子
#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:peson(std::string N_name);protected:std::string name;
private:};peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter:public peson
{
public:techter(std::string N_name);
protected:std::string tech_banji="我作为老师,教大学三年级";private:};techter::techter(std::string N_name):peson(N_name)
{
}
//**********************************************************************************
class student:public peson
{
public:student(std::string N_name);
protected:std::string student_banji="我作为学生,在读清华博士";private:};student::student(std::string N_name):peson(N_name)
{
}
//******************************************************
class techer_student: public techter, public student
{
public:techer_student(std::string N_name);void jieshao();
protected:std::string techer_student_banji="我是学生也是老师";
private:};techer_student::techer_student(std::string N_name):techter( N_name), student(N_name)
{
}void techer_student::jieshao()
{std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';//多继承 既可以使用student 的保护 也可以使用techter的保护
}int main()
{ techer_student lin("彭冠霖");lin.jieshao();std::cout << "Hello World!\n";
}
30、虚继承
虚继承的作用
虚继承的主要作用是解决多重继承中的“菱形继承”问题。在多重继承中,当一个类继承了多个父类,而这些父类又继承了同一个基类时,会导致子类中存在多份基类的拷贝,这会造成对基类成员的访问存在多义性
void techer_student::jieshao()
{std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';techer_student::stud() ;//调用了名字techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字//多继承 既可以使用student 的保护 也可以使用techter的保护
}
对基类成员的访问存在多义性
造成了继承了多个名字;
如下程序;
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:peson(std::string N_name);protected:std::string name;
private:};peson::peson(std::string N_name)
{
}
//*******************************************************************************
class techter:public peson
{
public:techter(std::string N_name);
protected:std::string tech_banji="我作为老师,教大学三年级";void tech();
private:};techter::techter(std::string N_name):peson(N_name)
{name = N_name;
}
void techter::tech()
{std::cout << "我是" << name;
}
//**********************************************************************************
class student:public peson
{
public:student(std::string N_name);
protected:std::string student_banji="我作为学生,在读清华博士";void stud();
private:};student::student(std::string N_name):peson(N_name)
{name = N_name;
}
void student::stud()
{std::cout << "我是" << this->name;
}
//******************************************************
class techer_student: public techter, public student
{
public:techer_student(std::string N_name1, std::string N_name2);void jieshao();
protected:std::string techer_student_banji="我是学生也是老师";
private:};techer_student::techer_student(std::string N_name1,std::string N_name2):techter( N_name1), student(N_name2)
{
}void techer_student::jieshao()
{std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';techer_student::stud() ;//调用了名字techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字//多继承 既可以使用student 的保护 也可以使用techter的保护
}int main()
{ techer_student lin("彭冠霖","小彭");lin.jieshao();std::cout << "Hello World!\n";
}
虚继承,在每个继承的时候,在前面添加virtual
在子类的构造器添加基类继承 和父类
使用虚继承后的程序:和以上程序相比,没啥体现差别,除了关键字;
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:peson(std::string N_name);protected:std::string name;
private:};peson::peson(std::string N_name)
{
}
//*******************************************************************************class techter: virtual public peson
{
public:techter(std::string N_name);
protected:std::string tech_banji="我作为老师,教大学三年级";void tech();
private:};techter::techter(std::string N_name):peson(N_name)
{name = N_name;
}
void techter::tech()
{std::cout << "我是" << name;
}
//**********************************************************************************
class student: virtual public peson
{
public:student(std::string N_name);
protected:std::string student_banji="我作为学生,在读清华博士";void stud();
private:};student::student(std::string N_name):peson(N_name)
{name = N_name;
}
void student::stud()
{std::cout << "我是" << this->name;
}
//******************************************************
class techer_student:virtual public techter, virtual public student
{
public:techer_student(std::string name);void jieshao();
protected:std::string techer_student_banji="我是学生也是老师";
private:};techer_student::techer_student(std::string name):techter(name), student(name),peson(name)
{
}void techer_student::jieshao()
{std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';techer_student::stud() ;//调用了名字techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字//多继承 既可以使用student 的保护 也可以使用techter的保护
}int main()
{ techer_student lin("彭冠霖");lin.jieshao();std::cout << "Hello World!\n";
}
再对比如下,效果一样;所以没体现出虚继承的差别;很疑惑
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include<string>
//*********************************************************************
class peson
{
public:peson(std::string N_name);protected:std::string name;
private:};peson::peson(std::string N_name)
{
}
//*******************************************************************************class techter: public peson
{
public:techter(std::string N_name);
protected:std::string tech_banji="我作为老师,教大学三年级";void tech();
private:};techter::techter(std::string N_name):peson(N_name)
{name = N_name;
}
void techter::tech()
{std::cout << "我是" << name;
}
//**********************************************************************************
class student: public peson
{
public:student(std::string N_name);
protected:std::string student_banji="我作为学生,在读清华博士";void stud();
private:};student::student(std::string N_name):peson(N_name)
{name = N_name;
}
void student::stud()
{std::cout << "我是" << this->name;
}
//******************************************************
class techer_student: public techter, public student
{
public:techer_student(std::string name);void jieshao();
protected:std::string techer_student_banji="我是学生也是老师";
private:};techer_student::techer_student(std::string name):techter(name), student(name)//,peson(name)
{
}void techer_student::jieshao()
{std::cout << student::student_banji << '\n' << techter::tech_banji << '\n' << techer_student::techer_student_banji << '\n';techer_student::stud() ;//调用了名字techer_student::tech();调用了名字 本来就是同一个人,只能用同一个名字,在继承过来后应该统一名字//多继承 既可以使用student 的保护 也可以使用techter的保护
}int main()
{ techer_student lin("彭冠霖");lin.jieshao();std::cout << "Hello World!\n";
}