继承(c++)

一、继承的相关概念

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类

继承是继承不了类的私有数据的。

继承方式有:公有继续,私有继承,受保护继承

如果省略继承方式就是默认为私有继承。和访问修饰符一样,不写就是默认私有。最好不要继承太多父类,占用内存,而且任意函数重复。

只继承别的父类来用就是继承,在父类的基础上进行功能新增和扩展就是派生。

多继承

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

其中,访问修饰符继承方式是 public、protectedprivate 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔。

class person
{
private:
	int id;
public:
	string name;
	int age;
	person()
	{
		name = "pyg";
		age = 12;
	}
	void test()
	{
		cout << "from father class" << endl;
	}
};
//访问属性就是在子类里的数据形式,如果继承一个父类是单继承,继承多个父类叫多继承
class stu:public person
{
public:
	//子类可以新增功能
	void learn()
	{
		cout << "我爱学习" << endl;
	}

};

int main()
{
	//通过继承子类可以使用父类在所包含的成员,子类不需要从0做起,可以用父类的数据
	stu s1;
	s1.test();//from father class
	s1.learn();//我爱学习
	cout << sizeof(s1) << endl;//父类的私有成员虽然用不了,但是会占内存,所以不要继承太多类

	return 0;
}

二、继承类型

当一个类派生自基类,该基类可以被继承为 public、protectedprivate 几种类型。我们几乎不使用 protectedprivate 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。

  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有保护成员将成为派生类的保护成员。

  • 私有继承(private):当一个类派生自私有基类时,基类的公有保护成员将成为派生类的私有成员。

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。

  • 基类的重载运算符。

  • 基类的友元函数。

类的各成员是否可被使用:私有只有自己能用

三、类的派生

派生类经历三个步骤:

  • 吸收基类成员

  • 改造基类成员

  • 添加新的成员

四、类的派生对象

派生类对象在创建时,会触发父类的构造函数

如果父类没有默认构造又有构造函数,也是要给父类传参的。

class person
{
private:
	int id;
public:
	string name;
	int age;
	person(string name,int age)
	{
		this->name = name;
		this->age = age;
		cout << "person struct" << endl;
	}
	/*person()
	{
		cout << "person struct" << endl;
	}*/
	void test()
	{
		cout << "from father class" << endl;
	}
};
//访问属性就是在子类里的数据形式,如果继承一个父类是单继承,继承多个父类叫多继承
class stu:public person
{
	int sorce;
public:
	//子类可以新增功能
	void learn()
	{
		cout << "我爱学习" << endl;
	}
	//父类没有默认构造,派生类构造函数要把数据传给父类,也要给自己传参初始化
	//初始化列表也可以给父类的构造初始化,进行传参
	stu(string name,int age,int sorce):person(name,age)
	{
		this->sorce = sorce;
		cout << "派生类构造" << endl;
	}
	void print()
	{
		cout << name << " " << age << " " << sorce << endl;
	}
};

int main()
{
	//通过继承子类可以使用父类在所包含的成员,子类不需要从0做起,可以用父类的数据
	//创建派生类对象时,先调父类的构造函数,在创建的时候给stu类型传参不行,要通过person类型传参
	stu s1("pyg",25,99);
	s1.test();//from father class
	s1.learn();//我爱学习
	cout << sizeof(s1) << endl;//父类的私有成员虽然用不了,但是会占内存,所以不要继承太多类
	s1.print();
	return 0;
}

如果父类没有默认构造要传参,就要在派生类构造的初始化列表传给父类。

一般在类内声明不用加初始化列表,在类外时才加:

类内:
stu(string name, int age, int sorce);
类外:
stu::stu(string name, int age, int sorce) :person(name, age)
{
	this->sorce = sorce;
	cout << "派生类构造" << endl;
}

五、把数值类型转为string

  //string===数值类型
 
    // int,float double -> string    to_string()
    string str1= to_string(12);
    string str2 = to_string(52.3f);
    cout << str1 << str2 << endl;
    //string -> int double  stoi  stof
    string temp = "12";
        cout << 12 + stoi(temp) << endl;//
//在派生类对象销毁时 析构的调用顺序

class father
{
public:

    father()
    {
        cout << "我是father的构造函数" << endl;


    }
    ~father()
    {
        cout << "我是father的析构函数" << endl;


    }


};


class mother
{
public:

    mother()
    {
        cout << "我是mother的构造函数" << endl;


    }
    ~mother()
    {
        cout << "我是mother的析构函数" << endl;


    }
};

//当派生类继承多个父类,此时派生类对象在删除时,对于父类的析构调用顺序与构造的调用顺序刚好相反。
class son :public father,public mother
{
public:
        
    son()
    {
        cout << "son的构造函数" << endl;
    }

    ~son()
    {
        cout << "son的析构函数" << endl;
    }


};


int main()
{
    son myson; //派生类在构建对象时,先调用父类的构造,再调用派生类的构造函数,当派生类对象销毁时,先调用派生类的析构最后调用父类的析构

    return 0;
}
//我是father的构造函数
//我是mother的构造函数
//son的构造函数
// 
//son的析构函数
//我是mother的析构函数
//我是father的析构函数

六、二义性问题

//子类的成员可以和父类中成员出现重名
class father
{
public:

    father()
    {
        cout << "我是father的构造函数" << endl;


    }
    void test()
    {
        cout << "from father 中的method" << endl;
    
    }
    ~father()
    {
        cout << "我是father的析构函数" << endl;


    }


};


//当派生类继承多个父类,此时派生类对象在删除时,对于父类的析构调用顺序与构造的调用顺序刚好相反。
class son :public father
{
public:
        
    son()
    {
        cout << "son的构造函数" << endl;
    }

    void test()
    {
        cout << "from son 中的method" << endl;

    }

    ~son()
    {
        cout << "son的析构函数" << endl;
    }


};


int main()
{
    son myson; 
    myson.test();//当派生类中的成员与父类中的成员如果出现重名,此时派生类对象在调用该同名成员时,优先调用自身的同名成员

    return 0;
}
class father
{
public:

    father()
    {
        cout << "我是father的构造函数" << endl;


    }
    void test()
    {
        cout << "from father 中的method" << endl;
    
    }
    ~father()
    {
        cout << "我是father的析构函数" << endl;


    }


};


class mother
{
public:

    mother()
    {
        cout << "我是mother的构造函数" << endl;


    }

    void test()
    {
        cout << "from mother 中的method" << endl;

    }
    ~mother()
    {
        cout << "我是mother的析构函数" << endl;


    }
};

//当派生类继承多个父类,此时派生类对象在删除时,对于父类的析构调用顺序与构造的调用顺序刚好相反。
class son :public father,public mother
{
public:
        
    son()
    {
        cout << "son的构造函数" << endl;
    }

    void test()
    {
        cout << "from son 中的method" << endl;

    }

    ~son()
    {
        cout << "son的析构函数" << endl;
    }


};


int main()
{
    son myson; 
    myson.father::test();//二义性问题多发生在多继承的场景下,当多个父类中出现相同的成员函数,此时派生类对象在调用该同名成员函数时出现访问的不确定性
    myson.mother::test();//1.出现二义性问题的解决方法: 派生类对象在调用同名成员时,在成员名前加上父类名做限定
                                   

    myson.test();  //2.可以在派生类中对父类的同名方法进行重新实现,这样 派生类对象在调用同名成员,会优先调用派生类的
    return 0;
}

//我是father的构造函数
//我是mother的构造函数
//son的构造函数
//from father 中的method
//from mother 中的method
//from son 中的method
//son的析构函数
//我是mother的析构函数
//我是father的析构函数

菱形继承

继承(c++)_第1张图片

//菱形继承-----危害:数据冗余(占用过多的内存),
//造成底层类对象 在访问最顶层类的数据成员时,出现访问的不确定性
class base
{
public:
    int a;
    int b;
    int c;
    int d;


};

//对于菱形继承的场景 需要中间类采用虚继承   通过虚继承,可以让底层类d 只获取一份a 的数据成员
class b:virtual public base
{



};

class c :virtual public base
{



};

class d :public b, public c
{


};



int main()
{
    d  dd;
    cout << sizeof(dd) << endl;//8  说明了 dd对象存放了两份Base中的数据成员

    dd.a;

    return 0;
}

七、基类与派生类的转换

不同类型数据间在一定条件下可以进行类型的转换。这种不同类型之间的转换和赋值称为赋值兼容。基类与派生类型对象之间也有赋值兼容关系,因为派生类中包含从基类继承的成员,因此可以将派生类的值赋值给基类对象

class base
{
public:
    int a;
    base()
    {
        a = 10;
    }

};



class child :public base
{
public:
    int c;
    child()
    {
        c = 0;
    }

};



int main()
{

    base a;
    child c;
    a = c;//派生类对象可以给基类对象进行赋值,因为赋值的兼容性--父类类型兼容子类类型
   // c = a;//不可以将基类对象给派生类对象赋值。

    base* pt = &c;//也可以通过基类指针或基类引用来绑定派生类对象
    return 0;
}
class Date
{
    public:
        int year;
        int month;
        Date(int y,int m)
        {
            this->year = y;
            this->month = m;
        }

        void print()
        {
            cout << year << month;
        }

};

class stu
{
public:
    string name;
    Date bir;//用另一个类的对象做为当前类中的数据成员,这个就叫做类的组合
    stu(string n, int y, int m) :bir(y, m)
    {
        name = n;
    
    }

    void test()
    {
        bir.print();//通过类的组合也可以实现对另一个类代码的复用
        cout << bir.month;
    }


};



int main()
{

    
    return 0;
}

你可能感兴趣的:(c++,算法,开发语言)