构造函数的继承 C#
- 格式:docx
- 大小:18.20 KB
- 文档页数:4
一.单项选择题(每题2分,共30分)1.若数组a定义为int[][]a=new int[3][4],则a是(B )。
A.一维数组B.二维数组C.三维数组D.四维数组2.下列关于for循环和while循环的说法中(A )是正确的。
A、while循环能实现的操作,for循环也都能实现B、while循环判断条件一般是程序结果,for循环判断条件一般是非程序结果C、两种循环任何时候都可替换D、两种循环结构中循环体都不可以为空3.void 的含义为(C )。
A.方法体为空B.定义的方法没有形参C.定义的方法没有返回值D.方法的返回值不能参加算术运算4.以下有关类的构造函数继承的说明,正确的是(C )。
A.子类继承父类的构造函数,但不能自己定义B.子类可定义自己的构造函数,但受父类定义的构造函数的限制C.子类和父类均可独自定义自己的构造函数,但执行时的顺序有规定D.如父类定义了构造函数,则子类只能继承父类的构造函数,不能自己定义5.下面说法正确的是(C )。
A.Java语言里的线程不是对象B.Java中任何可执行的类都需要main方法C.Java语言的类都是ng.Object的子类D.double是Java语言合法的标识符6.下列属于容器的组件有(B )。
A.Button B.Panel C.线程D.TextArea7.在Java语言中,不允许使用指针体现出的Java特性是( D )。
A.可移植B.解释执行C.健壮性D.安全性8.下列方法中,不属于WindowListener接口的是(D )。
A.windowOpened() B.windowClosed()C.windowActivated() D.mouseDragged()9.下列关于Applet的说法中,错误的是(C )。
A.Applet自身不能运行,必须嵌入到其它应用程序(如浏览器)中运行;B.可以在安全策略的控制下读写本地磁盘文件;C.Java中不支持向Applet传递参数;D.Applet的主类要定义为java.applet.Applet类的子类10.在Applet中显示文字、图形等信息时,应使用的方法是( A )。
前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。
构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。
在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。
这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数。
下面的例子展示了如何在派生类的构造函数中调用基类的构造函数:1.#include<iostream>ing namespace std;3.4.//基类People5.class People{6.protected:7.char*m_name;8.int m_age;9.public:10.People(char*,int);11.};12.People::People(char*name,int age):m_name(name),m_age(age){}13.14.//派生类Student15.class Student:public People{16.private:17.float m_score;18.public:19.Student(char*name,int age,float score);20.void display();21.};22.//People(name, age)就是调用基类的构造函数23.Student::Student(char*name,int age,float score):People(name, age),m_score(score){}24.void Student::display(){25.cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。
C++——类继承以及类初始化顺序对于类以及类继承, ⼏个主要的问题:1) 继承⽅式: public/protected/private继承.这是c++搞的, 实际上继承⽅式是⼀种允许⼦类控制的思想. ⼦类通过public继承, 可以把基类真实还原, ⽽private继承则完全把基类屏蔽掉.这种屏蔽是相对于对象层⽽⾔的, 就是说⼦类的对象完全看不到基类的⽅法, 如果继承⽅式是private的话, 即使⽅法在基类中为public的⽅法.但继承⽅式并不影响垂直⽅向的访问特性, 那就是⼦类的函数对基类的成员访问是不受继承⽅式的影响的.⽐较(java): java是简化的, 其实可认为是c++中的public继承. 实在没必要搞private/protected继承, 因为如果想控制,就直接在基类控制就好了.2) 对象初始化顺序: c++搞了个成员初始化列表, 并确明确区分初时化跟赋值的区别. c++对象的初始化顺序是:(a) 基类初始化(b) 对象成员初时化(c) 构造函数的赋值语句举例:假设 class C : public A, public B {D d;//}则初始化的顺序是A, B, D, C的构造函数.这⾥基类的初始化顺序是按照声明的顺序, 成员对象也是按照声明的顺序. 因此 c(int i, int j) : B(i), A(j) {} //这⾥成员初始化列表的顺序是不起作⽤的;析构函数的顺序则刚好是调过来, 构造/析构顺序可看作是⼀种栈的顺序;⽐较(java): java中初始化赋值是⼀回事. ⽽且对基类的构造函数调⽤必须显⽰声明, 按照你⾃⼰写的顺序.对成员对象, 也叫由你初始化.没有什么系统安排的顺序问题, 让你感觉很舒服;3) 多继承问题: c++⽀持多继承, 会导致"根"不唯⼀. ⽽java则没有该问题;此外c++没有统⼀的root object, java所有对象都存在Object类使得很多东西很⽅便. ⽐如公共的seriall, persistent等等.4) 继承中的重载: c++中, 派⽣类会继承所有基类的成员函数, 但构造函数, 析构函数除外.这意味着如果B 继承A, A(int i)是基类构造函数, 则⽆法B b(i)定义对象. 除⾮B也定义同样的构造函数.c++的理由是, 假如派⽣类定义了新成员, 则基类初始化函数⽆法初始化派⽣类的所有新增成员.⽐较(java): java中则不管, 就算有新增对象基类函数没有考虑到, ⼤不了就是null, 或者你⾃⼰有缺省值. 也是合理的.5) 继承中的同名覆盖和⼆义性: 同名覆盖的意思是说, 当派⽣类跟基类有完全⼀样的成员变量或者函数的时候, 派⽣类的会覆盖基类的.类似于同名的局部变量覆盖全局变量⼀样. 但被覆盖的基类成员还是可以访问的.如B继承A, A, B都有成员变量a,则B b, b.a为访问B的a, b.A::a 则为访问基类中的a. 这对于成员函数也成⽴.但需要注意的是, 同名函数必须要完全⼀样才能覆盖. int func(int j)跟int func(long j)其实是不⼀样的. 如果基类,派⽣类有这两个函数, 则不会同名覆盖.最重要的是, 两者也不构成重载函数. 因此假如A有函数int func(int j), B有函数int func(long j). 则B的对象b.func(int)调⽤为错误的. 因为B中的func跟它根本就不构成重载.同名覆盖导致的问题是⼆义性. 假如C->B=>A, 这⾥c继承B, B继承A. 假如A, B都有同样的成员fun, 则C的对象c.fun存在⼆义性. 它到底是指A 的还是B的fun呢?解决办法是⽤域限定符号c.A::fun来引⽤A的fun.另外⼀个导致⼆义性的是多重继承. 假设B1, B2都继承⾃B, D则继承B1, B2. 那么D有两个B⽽产⽣⼆义性.这种情况的解决办法是⽤虚基类. class B1 : virtual public B, class B2:virtual public B, D则为class D : public B1, public B2. 这样D中的成员只包含⼀份B的成员使得不会产⽣⼆义性.⽐较(java). java中是直接覆盖. 不给机会这么复杂, 还要保存基类同名的东西. 同名的就直接覆盖, 没有同名的就直接继承.虚基类的加⼊, 也影响到类的初始化顺序. 原则是每个派⽣类的成员化初始化列表都必须包含对虚基类的初始化.最终初始化的时候, 只有真正实例化对象的类的调⽤会起作⽤. 其它类的对虚基类的调⽤都是被忽略的. 这可以保证虚基类只会被初始化⼀次.c++没有显式接⼝的概念, 我觉得是c++语⾔的败点. 这也是导致c++要⽀持组件级的重⽤⾮常⿇烦. 虽然没有显式的接⼝, 但c++中的纯虚函数以及抽象类的⽀持, 事实上是等同于接⼝设施的. 当⼀个类中, 所有成员函数都是纯虚函数, 则该类其实就是接⼝.java c++接⼝类(所有成员函数都是纯虚函数)抽象类类(部分函数是虚函数)对象类对象类C++构造函数调⽤顺序1. 如果类⾥⾯有成员类,成员类的构造函数优先被调⽤;2. 创建派⽣类的对象,基类的构造函数优先被调⽤(也优先于派⽣类⾥的成员类);3. 基类构造函数如果有多个基类,则构造函数的调⽤顺序是某类在类派⽣表中出现的顺序⽽不是它们在成员初始化表中的顺序;4. 成员类对象构造函数如果有多个成员类对象,则构造函数的调⽤顺序是对象在类中被声明的顺序⽽不是它们出现在成员初始化表中的顺序;5. 派⽣类构造函数,作为⼀般规则派⽣类构造函数应该不能直接向⼀个基类数据成员赋值⽽是把值传递给适当的基类构造函数,否则两个类的实现变成紧耦合的(tightly coupled)将更加难于正确地修改或扩展基类的实现。
C++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执⾏顺序和执⾏内容⼀、本⽂⽬的与说明1. 本⽂⽬的:理清在各种继承时,构造函数、复制构造函数、赋值操作符、析构函数的执⾏顺序和执⾏内容。
2. 说明:虽然复制构造函数属于构造函数的⼀种,有共同的地⽅,但是也具有⼀定的特殊性,所以在总结它的性质时将它单独列出来了。
3. 单继承、多继承、虚继承,既然都属于继承,那么虽然有⼀定的区别,但还是相同点⽐较多。
如果放在⼀块讲,但为了将内容制作成递进的,就分开了,对相同点进⾏重复,(⼤量的复制粘贴哈),但在不同点进⾏了标注。
注意:三块内容是逐步递进的如果你懂虚函数,那么单继承和多继承那块你就可以不看;如果你懂多继承,那单继承你就不要看了,⾄于虚继承就等你懂虚继承再回来看吧;如果你只懂单继承,那你就只看单继承就好。
⼆、基本知识1. 对于⼀个空类,例如:class EmptyClass{};虽然你没有声明任何函数,但是编译器会⾃动为你提供上⾯这四个⽅法。
class EmptyClass {public:EmptyClass(); // 默认构造函数EmptyClass(const EmptyClass &rhs); // 复制构造函数~EmptyClass(); // 析构函数EmptyClass& operator=(const EmptyClass &rhs); // 赋值运算符}对于这四个⽅法的任何⼀个,你的类如果没有声明,那么编译器就会⾃动为你对应的提供⼀个默认的(注意合成默认构造函数是⽤于没有编写构造函数编译器才会合成默认构造函数,其中复制构造函数也是构造函数)。
(在《C++ primer》中,这个编译器⾃动提供的版本叫做“合成的***”,例如合成的复制构造函数)当然如果你显式声明了,编译器就不会再提供相应的⽅法。
2. 合成的默认构造函数执⾏内容:如果有⽗类,就先调⽤⽗类的默认构造函数。
继承类的构造函数
一、继承类的构造函数
1、概念
继承类的构造函数是指继承类继承了基类的数据成员和成员函数,但又要完成一定的初始化工作,满足自己的要求时,它就需要有自己的构造函数来完成这件事情。
2、使用方法
在构造函数中,首先要通过使用基类的构造函数,来完成基类的数据成员的初始化,这样可以确保基类数据成员的正确性。
然后再使用继承类的构造函数来完成自己的初始化工作。
例如,有一个类 Line,它继承了 Point 类:
class Line:public Point
{
public:
Line(int x1,int y1,int x2,int y2):Point(x1,y1)
{
m_x2 = x2;
m_y2 = y2;
}
private:
int m_x2;
int m_y2;
};
在上面的构造函数中,首先要使用基类 Point 的构造函数
Point(x1, y1) 来完成基类 Point 的数据成员的初始化,然后在继承类 Line 的构造函数中,再完成继承类 Line 自己的数据 m_x2 和m_y2 的初始化。
3、注意事项
(1)在 C++ 中,如果在子类的构造函数中没有显式的使用基类的构造函数,编译器会自动添加一个默认的基类构造函数,将基类成员变量设置为默认值。
(2)当子类构造函数中显式调用基类的构造函数时,则子类的成员变量将被赋予与基类构造函数相同的值。
(3)建议在子类的构造函数中显式地调用基类的构造函数,这能确保基类构造函数将被正确的调用,而不会出现意外的情况出现。
C++习题3(继承和多态)C++习题3(继承和多态)一、选择题1、在C++中,类与类之间的继承关系具有( C )A)自反性 B)对称性 C)传递性 D)反对称性2、在公有继承的情况下,基类的成员(私有的除外)在派生类中的访问权限( B )A)受限制 B)保持不变 C)受保护 D)不受保护3、按解释中的要求在下列程序划线处填入的正确语句是:( C )#include class Base{public:void fun(){cout<<"Base::fun"<<=""> fun()cout<<"Derived::fun"<fun();4、在保护继承的情况下,基类的成员(私有的除外)在派生类中的访问权限( C )A)受限制 B)保持不变 C)受保护 D)不受保护5、在哪种派生方式中,派生类可以访问基类中的protected 成员(B )A)public和private B)public、protected和private C)protected和private D)仅protected6、当一个派生类仅有protected继承一个基类时,基类中的所有公有成员成为派生类的(C)A)public成员 B)private成员 C)protected成员 D)友元7、不论派生类以何种方法继承基类,都不能使用基类的(B )A)public成员 B)private成员 C)protected成员 D)public成员和protected成员8下面叙述错误的是(S )。
A )基类的protected成员在派生类中仍然是protected的 B)基类的protected成员在public派生类中仍然是protected的C)基类的protected成员在private派生类中是private的 D)基类的protected 成员不能被派生类的对象访问9、下列说法中错误的是(S )。
子类继承父类构造函数
在C++中,子类继承了父类的构造函数。
当创建一个子类的对象时,父类的构造函数会被自动调用。
如果父类有多个构造函数,那么子类会继承所有这些构造函数。
在子类的构造函数中,可以通过使用super()函数来调用父类的构造函数。
这个函数必须在子类构造函数的第一行调用。
下面是一个例子:
class Parent {
public:
Parent(int a) {
// 父类的构造函数
}
};
class Child : public Parent {
public:
Child(int a, int b) : Parent(a) {
// 子类的构造函数,它调用了父类的构造函数
}
};
在这个例子中,Child类继承了Parent类,并且定义了一个自己的构造函数。
这个子类的构造函数在第一行调用了Parent类的构造函数。
这意味着当创建一个Child对象时,Parent类的构造函数也会被调用。
请注意,如果在子类的构造函数中没有显式地调用父类的构造函数,那么编译器会默认调用父类的无参数构造函数。
如果父类没有无参数的构造函数,那么必须显式地调用父类的某个构造函数。
1、在C++中,关于类的构造函数,下列说法错误的是:A. 构造函数的名字必须与类名完全相同B. 构造函数在对象创建时自动被调用C. 构造函数可以有返回类型D. 一个类可以有多个构造函数,通过重载实现(答案:C)2、下列关于C++中继承的说法,正确的是:A. 子类只能继承父类的一个构造函数B. 子类不能访问父类的私有成员C. 子类可以重写父类的所有成员函数,无论其访问权限如何D. C++支持多重继承,即一个子类可以同时继承多个父类(答案:D)3、在C++中,关于动态内存分配,下列说法正确的是:A. 使用new操作符分配的内存,不需要显式释放B. malloc和free是C++中推荐的动态内存分配和释放方式C. 使用delete操作符释放内存后,指针仍然指向原来的内存地址D. 动态分配的内存如果不使用delete或delete[]释放,可能会导致内存泄漏(答案:D)4、在C++中,下列哪个关键字用于声明一个虚函数?A. overrideB. finalC. virtualD. abstract(答案:C)5、关于C++中的模板,下列说法错误的是:A. 模板可以用于函数和类B. 模板实例化时,类型参数可以被具体类型替换C. 模板参数只能是类型参数,不能是值参数D. 使用模板可以提高代码的复用性和可维护性(答案:C)6、在C++中,关于异常处理,下列说法正确的是:A. try块中必须至少有一个catch块与之对应B. throw关键字用于抛出异常,其后可以跟任意类型的对象C. catch块只能捕获特定类型的异常D. 异常处理机制可以替代传统的错误返回码方式,使代码更清晰(答案:D)7、在C++中,关于STL(标准模板库)中的vector,下列说法错误的是:A. vector是一个动态数组,可以根据需要自动调整大小B. vector中的元素在内存中是连续存储的C. vector的push_back操作会在向量的开头插入一个新元素D. 使用vector之前需要包含头文件<vector>(答案:C)8、在C++中,关于多态性,下列说法正确的是:A. 多态性只能通过继承实现B. 多态性允许通过基类指针调用派生类的成员函数C. 多态性要求基类中的成员函数必须是虚函数D. 实现多态性时,基类的析构函数也应该是虚函数,以防止资源泄漏(答案:D)。
c类的继承和多态例子继承是面向对象编程中的重要概念之一,它允许一个类“继承”另一个类的属性和方法。
在C++中,继承分为三种类型:公有继承、私有继承和保护继承。
其中,公有继承是最常用的一种方式,也是实现多态的基础。
本文将通过一个例子来介绍C++中的公有继承和多态特性。
假设我们要设计一个动物园的系统,其中包含不同类型的动物。
首先,我们定义一个基类Animal,代表所有动物的共有属性和方法。
然后,派生出几个具体的动物类,如Lion(狮子)、Elephant (大象)和Monkey(猴子),它们都是Animal类的派生类。
1. 基类Animal的定义:```c++class Animal {public:Animal() {} // 构造函数virtual ~Animal() {} // 虚析构函数virtual void move() const = 0; // 纯虚函数,用于表示不同动物的移动方式protected:int age; // 年龄double weight; // 体重};```2. 派生类Lion的定义:```c++class Lion : public Animal {public:Lion(int a, double w) : Animal(), color("yellow") { age = a;weight = w;}void move() const {std::cout << "Lion is running." << std::endl;}private:std::string color; // 颜色};```3. 派生类Elephant的定义:```c++class Elephant : public Animal {public:Elephant(int a, double w) : Animal(), height(3.5) { age = a;weight = w;}void move() const {std::cout << "Elephant is walking." << std::endl; }private:double height; // 身高};```4. 派生类Monkey的定义:```c++class Monkey : public Animal {public:Monkey(int a, double w) : Animal(), num_bananas(5) {age = a;weight = w;}void move() const {std::cout << "Monkey is jumping." << std::endl;}private:int num_bananas; // 香蕉数目};```以上就是实现动物园系统的基本类定义。
基类成员的初始化工作由基类的构造函数完成,派生类的初始化工作则有派生类的构造函数完成,这就产生了派生类构造函数的执行顺序问题,即当创建一个派生类的对象时,如何调用基类和派生类的构造函数分别完成各自成员的初始化。
派生类中的构造函数:如果基类没有定义构造函数,派生类也可以不定义构造函数,全都采用默认的构造函数,此时,派生类新增成员的初始化工作可用其他公有函数来完成,如果只有派生类定义构造函数时,只需构造派生类对象即可。
对象的基类部分使用默认构造函数来自动创建。
基类中的构造函数:当基类和派生类都定义有构造函数时,情况就变得复杂了,同时也存在着继承规则。
1.如果基类中定义了构造函数,并且此构造函数没有参数,那么他可以隐式的被派生类继承,也就是说,派生类根本不需要包含构造函数,如下例子:namespace Syntaxtest{class Personclass{protected Personclass(){Console.WriteLine("基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy被À?继¨¬承D!ê?");}}class Studentclass : Personclass{}class Program{static void Main(string[] args){Studentclass t1 = new Studentclass();}}}运行结果:在基类中构造函数被继承从运行结果来看,基类没有参数的构造函数被无条件继承,在派生类中被调用。
2.如果基类定义了带有参数的构造函数,则此构造函数必须被继承且在派生类中实现构造函数,提供一个将参数传递给基类构造函数的途径,以保证在基类进行初始化时能获得必须的数据,在实现构造函数时我们可以使用base关键字。
代码如下:namespace Syntaxtest{class Personclass{protected Personclass(string T,string M){Console.WriteLine("基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?");Console.WriteLine("基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy被À?继¨¬承D!ê?");Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}",T);Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}",M);}}class Studentclass : Personclass{public Studentclass(string N, string I):base(N,I){Console.WriteLine("派¨¦生¦¨²类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?");Console.WriteLine("参?数ºy设¦¨¨置?的Ì?ID是º?:{0}", I);Console.WriteLine("参?数ºy设¦¨¨置?的Ì?姓?名?是º?:{0}", N);}}class Program{static void Main(string[] args){Studentclass t1 = new Studentclass("xusen","2010");}}运行结果:基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy被À?继¨¬承D!ê传递的参数:xusen传递的参数:2010派¨¦生¦¨²类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?参数设置的姓名是:xusen参数设置的ID是:2010派生类隐式继承基类中带有参数的构造函数,在程序中基类定义了带有参数的构造函数,在其派生类中被继承,并使用base关键字调用基类中的构造函数来传送参数。
我们可以从代码中看到在创建派生类的对象后,程序首先运行的是基类的构造函数中的内容,然后才是派生类中的内容。
3.基类中是没有参数的构造函数,在派生类中可以自定义有参数的构造函数如果派生类的基类也是派生类,则每个派生类只需负责其直接基类的构造,不负责间接基类的构造,并且其执行构造函数的顺序是从最上面的基类开始的,直到最后一个派生类结束。
代码如下:namespace Syntaxtest{class Firstclass{protected Firstclass(string A, string B){Console.WriteLine("第̨²一°?个?基¨´类¤¨¤中D的Ì?内¨²容¨Y!ê?");Console.WriteLine("第̨²一°?个?基¨´类¤¨¤中D的Ì?内¨²容¨Y被À?继¨¬承D!ê?");Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}", A);Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}", B);}}class Personclass:Firstclass{protected Personclass(string T,string M):base(T,M){Console.WriteLine("基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?");Console.WriteLine("基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy被À?继¨¬承D!ê?");Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}",T);Console.WriteLine("传ä?递ÌY的Ì?参?数ºy:{0}",M);}}class Studentclass : Personclass{public Studentclass(string N, string I):base(N,I){Console.WriteLine("派¨¦生¦¨²类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?");Console.WriteLine("参?数ºy设¦¨¨置?的Ì?ID是º?:{0}", I);Console.WriteLine("参?数ºy设¦¨¨置?的Ì?姓?名?是º?:{0}", N);}}class Program{static void Main(string[] args){Studentclass t1 = new Studentclass("xusen","2010");}}}运行结果:第一个基类中的内容第一个基类中的内容被继承传递的参数:xusen传递的参数:2010基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?基¨´类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy被À?继¨¬承D!ê传递的参数:xusen传递的参数:2010派¨¦生¦¨²类¤¨¤中D的Ì?构1造¨¬函¡¥数ºy的Ì?内¨²容¨Y!ê?参数设置的姓名是:xusen参数设置的ID是:2010。