C++:随笔5---this指针和类的继承

    科技2022-07-11  108

    this指针:(通过一个典型的例子来认识他)

    class Human { char fishc; Human(char fishc);//构造函数 }; Human::Human(char fishc)//对构造函数进行初始化 { fishc=fishc;//意图就是把这个传入参数赋值给这个上边类属性的fishc } //但是这样赋值的话,他们的名字一样,这样的话构造器就有可能认不出来,(因为他不知道你是要把属性去覆盖参数,还是把传入的参数去覆盖给属性,因为两者的名字一样,但是语法没有错。他们是两个不同区域的一个是传入的参数,一个是类的属性,(因为他们位于两个不同的区域,所以语法上没有错)) //所以怎么让构造器知道哪个是参数哪个是属性呢?这个时候就需要用到this指针。 this->fishc=fishc;//this指针是指向当前的类生成的对象//所以就很明显前者是类的属性,后边是参数。 //这样的话编译器就懂了,赋值操作符的左边将被解释为当前对象的fishc属性,右边将被解释为构造器的传入来的fishc的参数。

    //PS:注意使用this指针的基本原则是如果代码不存在二义性隐患,就不必使用this指针。

    ------------类的继承-------

    继承机制使得程序员可以创建一个类的堆叠层次结构,每个子类均将继承在他的基类里定义的方法和属性。(在继承原有方法的同时,再增加另外的一些属性的方法)(通过继承机制可以对现有的代码进行进一步的扩展,并应用在新的程序中)

    (1)他们都是动物的子类。我们可以编写一个Animal作为Turtle和Pig的基类。

    继承类的语法:

    //语法: class SubClass:public Superclass{...} //例子: class Pig:public Animal{...}

    基类是可以派生出其他的类,也称为父类和超类;子类是从基类派生出来的类(比如Turtle类和Pig类)。

      

    实现如下:

    #include<iostream> #include<string> class Anmial//声明一个基类 { public: std::string mouth; void eat(); void sleep(); void drool(); }; //继承基类 class Pig:public Animal { public: void climb(); }; class Turtle:public Animal { public: void swim(); }; //对上述方法进行补充 void Animal::eat() { std::cout<<"我正在吃"<<std::endl; } void Animal::sleep() { std::cout<<"我正在睡觉"<<std::endl; } void Animal::eat() { std::cout<<"我正在流口水"<<std::endl; }//上述把基类的三个方法给搞定了 //下面实现子类的方法(不管他们是基类还是父类,他们都是方法就按照类的方法实现来写) void Pig::climb() { std::cout<<"我会爬树"<<std::endl; } void Turtle::swim() { std::cout<<"我会游泳"<<std::endl; } int main() { //对类进行实例化 Pig pig; Turtle turtle; pig.sleep(); turtle.sleep(); pig.climb(); turtle.swim(); }

    ------带参数的构造器----

    //构造器带着输入参数 //1、声明 class Animal { public: Animal(std::string thename); std::string name; }; class Pig:public Animal { public: Pig(std::string thename); }; //2、方法定义 Animal::Animal(std::string thename) { name=thename; } Pig::Pig(std::string thename):Animal(thename)//Pig的构造器继承Animal的构造器 { }

    因为Pig的方法里面是空的,他是直接继承于Animal的方法的。

    ------访问控制--------

    上述类的所有成员都是用public:语句声明。

    所谓的访问控制就是c++提供了一种用来保护类里的方法和属性的手段。

      

    -------覆盖------

    既有共同特征又需要在不同类里有不同的实现方法)意思就是在类里边重新声明一下这个方法,他的函数名字参数返回值一摸一样(不然的话就会变成重载了)。

    例子:为Animal添加eat方法,并在子类中进行覆盖。

    -------重载方法--------

    继承之后不能重载。(子类里边不能重载)

    ----友元-------

       

    声明另外一个类是这个类的友元类,友元关系是类之间的一种特殊关系,这种特殊关系不仅允许友元类访问对方的public方法和属性,还允许友元访问对方的protected和private方法和属性。

    声明一个友元关系的语法,在类声明里的某个地方加上一条friend class **就行了(其中**表示你需要友元的另一个类的那个名字)。

    注意这条语句可以放在任何地方,放在public,protected和private段落里边都可以。(这样之后他们两个人就相当于是同一个人)

    例子:定义Lovers这个类,有两个子类Boyfriend和Girlfriend。Lovers类的方法kiss()和ask();第三者Others类,想要kiss()Girlfriend类的对象。

    #include<stdio.h> #include<iostream> class Lovers { public: Lovers(std::string theName);//构造函数,就是给他命名 void kiss(Lovers* lover);//参数声明一个指针, void ask(Lovers* lover,std::string something); protected: std::string name; friend class others;//这时候others跟Lovers就是一对。 }; class Boyfriend:public Lovers { public: Boyfriend(std::string theName); }; class Girlfriend:public Lovers { public: Girlfriend(std::string theName); }; class Others { public: Others(std::string theName); void kiss(Lovers* lover); protected: std::string name;//Lover->name属于这个类的protected }; Lovers::Lovers(std::string theName); { name=theName;//构造函数给它命名,也就是说我们造一个人出来的时候, } void Lovers::kiss(Lovers* lover) { std::cout<<name<<"亲亲"<<lover->name<<std::endl; } void Lovers::ask(Lovers* lover,std::string something) { std::cout<<Lover->name<<"帮我"<<something<<std::endl; } //子类 Boyfriend::Boyfriend(std::string name):Lovers(theName) { } Girlfriend::Girlfriend(std::string name):Lovers(theName) { } //友元类 Others::Others(std::string theName); { name=theName;//构造函数给它命名,也就是说我们造一个人出来的时候, } void Others::kiss(Lovers* lover)//这个里边的参数引用到了lover { std::cout<<name<<"吻一下"<<lover->name<<std::endl;//lover->name属于这个Lovers类的protected,不是这个类的子类根本访问不到,而others不是它的子类,所以理论上是访问不到的,这里因为上边是friend class others;所以可以访问到。 } //主函数 int main() { Boyfriend boyfriend("A君");//造一个人出来给它命名叫做A君(构造函数) Girlfriend girlfriend("B妞");//叫做B妞 Others others("路人甲");//others这个陌生人叫做路人甲 girlfriend.kiss(&boyfriend);//因为参数是指针所以传进去的应该是对象的地址 girlfriend.ask(&boyfriend,"拖地") std::cout<<"传说中的友元类,路人甲出现了\n"<<std::endl; others.kiss(&girlfriend); return 0; }

    -------静态属性和静态方法-------

    创建一个静态属性和静态方法:只需要在他的声明前加上static保留字即可。

    #include<string> #include<iostream> class Pet { public: Pet(std::string theName);//带参数的构造器 ~Pet(); static int getCount();//这里作为一个接口,用来给他由他生成的对象来获取他这个计数器 protected: std::string name; private://private是这个类里的方法才能够调用的,getCount()函数可以调用//private成员只有这个类里边的方法才能够访问它 static int count;//私有成员,一个count计数器 }; class Dog :public Pet//继承Pet类 { public: Dog(std::string theName); ~Dog(); }; class Cat :public Pet//继承Pet类 { public: Cat(std::string theName); ~Cat(); }; int Pet::count = 0;//这一句做了两件事;第一:让编译器为count这个变量的值分配内存;第二:这个变量因为是在静态存储区,他是把这个变量初始化为0; Pet::Pet(std::string theName)//构造函数,当该构造函数被调用的时候说明这个宠物已经被建造出来了 { name = theName; count++;//那么这个计数器就加一 std::cout << "一只宠物变出来了,名字叫做:" << name << std::endl; } Pet::~Pet()//析构器 { count--; //析构器说明这个宠物已经挂掉了,所以计数器要减掉 std::cout << name << "挂掉了" << std::endl; } int Pet::getCount()//这个接口函数getCount()的唯一作用,把这个count返回出来,把他拿到的这个私有成员返回出来,然后show出来 { return count;//是为了任何代码都可以通过调用getCount()函数(因为是public)从而来读取该属性count的值(因为该值是private,所以对该属性只能是读不能是写,只有在Pet的析构函数和构造函数才能够对他进行写) } Dog::Dog(std::string theName) :Pet(theName)//Dog的构造器继承Pet的构造器 { //std::cout<<"this:"<<this<<endl;//1生成一个dog对象之后就把this指针给打印出来//按此它生成一个dog对象的时候this指针应该是指向dog对象的。下边生成dog对象 } Dog::~Dog() { } Cat::Cat(std::string theName) :Pet(theName) { } Cat::~Cat() { } int main() { Dog dog("Tom");//整了一只狗dog叫做Tom(继承构造器的实现)//用Dog类生产出dog对象 Cat cat("Jerry");//整了一只猫叫做Jerry std::cout<<"dog:"<<&dog<<std::endl;//2这里生成dog对象之后,我们把dog也给打印出来 std::cout << "已经诞生了" << Pet::getCount() << "只宠物!\n\n";//显示出来//getCount()函数调用私有成员count {//注意这里的大括号,是有作用的。这里相当于是一个区域 Dog dog_2("Tom_2");//又整了一只狗 Cat cat_2("Jerry_2");//又整了一只猫 std::cout << "现在呢,已经诞生了" << Pet::getCount() << "只宠物!\n\n"; }//到此之前还剩下两只,因为大括号之后析构函数了,把宠物给毁掉 std::cout << "\n现在还剩下" << Pet::getCount() << "只宠物!\n\n"; return 0;//return 0的时候一只宠物都没有了,因为析构函数了,把宠物给毁掉 } //上述1和2处打印的地址完全一样

    结果:右边结果是去掉main函数中间的大括号的

       

    static静态变量的详解:https://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html

    静态成员是所有对象共享的,所以不能在静态方法里访问非静态的元素。

    非静态方法可以访问类的静态成员,也可以访问类的非静态成员。

    C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区:https://fishc.com.cn/blog-9-1097.html

    this指针:this指针是类的一个自动生成,自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象的地址。(也就是说我生成每一个对象的时候,它都会自动生成一个this指针,这个指针指向的是对象它的一个地址)当一个对象被创建时,该对象的this指针就会自动指向对象数据的首地址。

    #include<iostream> class Point { private: int x,y; public: point(int a,intb) { x=a; y=b } void MovePoint(int a,int b) { x=a; y=b; } void print() { std::cout<<"x="<<x<<"y="<<y<<endl; } }; int main() { Point point1(10,10);//用Point这个类生产出Point1这个对象。这个点的坐标在(10,10)这个位置 Point1.MovePoint(2,2); point1.print(); return 0; } //当对象point1调用MovePoint这个成员的时候,(实时上我们生成point1这个对象的时候,就有一个this指针指向point1的地址),当他调用MovePoint这个函数的时候,即将point1对象的地址传递给this指针,(因为我们需要用到的是point1这个对象的成员,那我们就必须知道这个对象的地址,就必须传递给this指针) //MovePoint函数的原型事实上应该是 void MovePoint(Point* this,int a,int b);他有三个参数,一个是隐含的this指针它指向Point对象(第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见是因为这个参数在类中是隐含的,是C++的一个规则,因为每一个都是默认添加进第一个规则)。 //这样子pOint1的地址就传递给this,所以在MovePoint()函数中便可以显示的写成void MovePoint(int a,int b)(this->x=a;this->y=b)(其实也可以x=a,这样编译器知道哪一个是参数哪一个是变量,用this做区分) //我们这样就可以知道point1调用该函数后,也就是point1的数据成员被调用并更新了值。

     

    Processed: 0.050, SQL: 8