关于C++继承你可能会忽视的点

目录
  • 前言
  • 一、什么是继承
  • 二、基类与派生类的赋值转换
    • 2.1天然支持的理解
  • 三、继承当中的作用域
  • 四、派生类的默认构造成员函数
    • 4.0什么时候需要写6个默认成员函数
    • 4.1构造函数
    • 4.2拷贝构造
    • 4.3赋值重载
  • 五、菱形继承和菱形虚拟继承
    • 5.1菱形继承
  • 六、继承的总结
    • 继承和组合
  • 总结

前言

继承是使代码复用的一种重要的手段,我们在C语言时期写的swap函数逻辑,通常会单独写出来再给其他函数复用,这个继承可以理解成是类级别的一个复用,它允许我们在原有类的基础上进行扩展,增加新的功能。

一、什么是继承

举个例子,当我们使用一个结构体去描述一个学生的信息时,我们可以用到以下的这样一个组织方式:

struct Student
{
	char sex[20];//性别
	int age;//年龄
	char stu_id;//学号
	//.....
};

当我们要描述一名老师的时候我们这个时候可能就是更改学生信息当中的部分的信息,例如上面的性别和年龄是可以通用的,而学号只需更改成工号即可。那么我们应该怎么去达到复用的逻辑呢?

:我们可以写一个struct People,让struct Student和struct Teacher去复用它。

struct People
{
	char sex[20];//性别
	int age;//年龄
};
struct Student
{
	struct People p;
	char stu_id;//学号
	//.....
};
struct Teacher
{
	struct People p;
	char work_id;//工号
	//.....
};

这样子我们用之前的C语言的知识就可以完成一个简单的复用,这样子做会有几个不好的地方:

  • 对于People内部的访问会比起访问他自己内部定义的变量麻烦一点(就是如定义了Teacher t;要访问age需要 t.p.age)。
  • 若基类(父类)想要对于派生类(子类)有所隐藏,即并不想让所有的成员函数/成员变量都给子类所继承的时候,我们用这种方式很难做到。

基于以上的问题,C++给出了一套继承逻辑。

上述第一个问题就解决了,我们可以直接在People t,直接访问基类的成员,第二个问题,对于一些我们想要隐藏的成员函数/成员变量(即不让子类可见),我们可以通过继承方式来控制,在此之前先铺垫一个知识点。

上图若是开过c++这门课的同学肯定都有见过,其中的最左列是表明基类的被继承的成员是什么类型的,第一行则是以哪种形式继承。

基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private 。

其中protected成员变量我们在继承这块见得会比较多,下面我们比较一下public/protected/private的在继承当中的作用。

  • 若基类的成员当中为public,则说明他支持类内,类外部都可以去访问他,而这时候我们常用public继承,因为基类都已经开放了这个成员变量,派生类用其他两种方式继承都会让他在派生类的类外部无法访问,所以实际上public继承是最常用的。
  • 若基类的成员当中是protected类型,表明基类允许子类当中可以使用这个成员,但是不希望类外部使用,protected的访问限定符只是对类外部上锁,而类内是可以随意使用的,这样实际上也是封装性的一种体现。
  • 若基类的成员是private类型,表明不希望子类和外部访问,在子类当中不可见,不可见即子类当中无法访问该成员。但是他是否存在于派生类当中?是存在的。

实践是检验真知的唯一标准,下面我们试试是否private的成员在子类真的存在:

class People
{
public:
	char address;//住址
protected:
	double sex;//性别
private:
	int age;//年龄
};
class Student : protected People
{

	char stu_id[20];//学号
	//.....
};
struct Teacher :public People
{
	double work_id;//工号
	//.....
};
int main()
{

	Teacher t;
	cout << sizeof(t) << endl;//对t变量的大小测试
	printf("%p,%p", &t.address,&t.work_id);

	return 0;
}

验证结果为32,即下图所示,我们可以得知People的元素是在Teacher之上的。

示意图:

倘若强行访问,则会报错。到此问题2的答案也清晰了。

不推荐protected继承的原因:

class People
{
public:
	char address;//住址
protected:
	double sex;//性别
private:
	int age;//年龄
};
struct Student : protected People
{

	char stu_id[20];//学号
	//.....
};
int main()
{
	Student s1;
	People p = s1;//error
	return 0;
}

上面这个protected继承后子类对象赋值给基类报错!

其实是因为父类的public对象address在以protected方式继承时相当于在类外部不可访问,当赋值给People对象时,权限被放大,即若支持赋值,子类address是不对外开放的,而父类却把成员变量公开了!

解决方案:使用public继承!

二、基类与派生类的赋值转换

2.1天然支持的理解

这个点是一个十分重要的点,在学习java语言的时候,经常听到上转型,其实也就是c++当中将子类的对象赋值给父类,即People t = student s;类似这种,这个过程是天然支持的,接下来叙述一下天然支持的含义。

预备知识

int i = 0;
double b = i;//1
double& b =i ;//2 error
const double& b = i ;//3

从初识c语言的时候,我们就发现上面代码的第一条是没有问题的,这是因为相近类型在精度低给精度高的时候是不会出现问题的。这是因为编译器会在此期间生成一块临时空间(临时空间具有常性),用i生成一个double类型的i再赋值给b。

在上面的2代码的时候为什么会出错呢?原因很简单,临时空间具有常性,临时空间是放到静态区当中的,不可修改,当用double&时相当于会对权限进行放大(即b可能会更改i的内容),所以我们加入const属性的时候代码3也就能够跑过了。

上面的子类给父类为什么就是天然支持的呢?

int main()
{
	Teacher t;
	People& p = t;//true
	People* p2 = &t;//true
	People p3 = t;//true
	return 0;
}

上面的程序正常运行,父类引用子类对象完全没有问题!所以我们才说这是天然支持的,不像上面例子是通过转换而来的。

这种派生类对象赋值给基类的对象/基类的指针/基类的引用,称之为切片,这种说法是十分贴切的。通过切去子类的自己定义的部分在给到基类。

这里会有一些值得注意的点:

基类的对象不能赋值给派生类对象!!

基类的指针是可以通过强制类型转换来赋值给派生类的指针,但是基类的指针必须原先是指向派生类对象才是安全的。倘若基类是多态类型,可以用RTTI当中的dynamic cast来进行识别后进行安全转换。(后序博客会将,这里简单说明就是使用dynamic cast,他会判断指针指向的是不是派生类对象,如果是就转换成功,不是就会返回null)

对于上面第二点做一个解释,就是如果People * pp指向的是一个People的对象,那么当他给到派生类的指针的时候,派生类指针是有可能访问到未初始化的那部分。因为站在派生类指针的角度,他并不知道自己的成员是没有被定义的,倘若它使用了未初始化数据,就会产生越界报错!!
相反,如果原先的pp指针指向的是派生类对象(天然支持的),那么当我们pp给到派生类的时候,对于派生类的而言,它的数据都是初始化好的,所以这个时候是没有问题的。

三、继承当中的作用域

在继承体系中基类和派生类都有独立的作用域!!

代码如下:

class People
{
public:
	char address[20] = "chang an";//住址
	void func(int i)
	{
		cout << "People func\n";
	}
protected:
	char sex[20] = "nan";//性别
private:
	int age = 19;//年龄
};
struct Student : public People
{

	char stu_id[20] = "1010";//学号
	//.....
	void func()
	{
		cout << "Student func\n";
	}
};

int main()
{
	People p;
	Student s;
	s.func(); //true
	s.func(1);//err
	return 0;
}

从上面的例子可以看出,倘若基类和子类都在同一个作用域,那么func的有参和无参是构成重载的,但是编译器这里报错,说明重载的一个重要条件不满足,即函数不在同一作用域当中。

这种子类成员将父类的成员屏蔽的情况,叫做隐藏,也叫重定义,倘若需要调用父类的函数需要在函数前显示调用(指名类域)即可。

注意:

  • 继承体系当中不建议定义同名的成员,因为会引发误解。
  • 但是在派生类的默认成员函数当中会用到这种语法,所以这种语法也是必不可少的!!
  • 成员函数和成员变量都如此,基类定义相同名字的都会对父类进行隐藏,调用都要显示调用。

四、派生类的默认构造成员函数

1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

5. 派生类对象初始化先调用基类构造再调派生类构造。

6. 派生类对象析构清理先调用派生类析构再调基类的析构。

总结前面的6个规则,个人的结论:

  • 构造函数,拷贝构造,operator=三种情况,都要调用父类对应的构造函数/拷贝构造/operator=进行对父类的成员变量的初始化,并且倘若父类没有默认的构造函数的时候(比如父类写了带参的构造函数),我们就要显式调用(Person(参数…),Person::operator=(参数…))
  • 析构函数只需要清理子类定义的资源,由于在构造函数当中我们是先对父类的成员先进行构造,后对子类的成员进行构造。由先构造后析构的顺序,所以我们是在析构函数当中析构子类的资源,析构函数调用完后编译器自动帮我们调用父类的析构函数。

在派生类当中基类为一个自定义类型,会自动调用父类的构造函数进行初始化。在拷贝构造当中,由于子类把父类的部分当做自定义类型,倘若没有显示调用拷贝构造,就会调用到构造函数上面对父类的部分进行构造。

在子类当中直接对父类单独的成员初始化是错误的,一定要把父类当做一个整体进行初始化!

4.0什么时候需要写6个默认成员函数

抛出结论:

1. 若父类没有默认构造函数/默认拷贝构造函数或者有需要对成员的初始化(可以在声明处给缺省值),或者编译器提供的浅拷贝行为不能满足我们的需求。

2. 当我们成员变量中采用T*,自己维护在堆上开的空间时,我们往往需要对除取地址重载外的其余默认成员函数进行编写。因为我们没有选择容器,自己动手维护堆上的资源时,若采用编译器默认生成的值拷贝的方式,分分钟出错!

4.1构造函数

#include<iostream>
using namespace std;
class People
{
public:
	People()
	{
		cout << "People()\n";
	}
	//p1(p)
	People(const People& p)
	{
		cout << "People(const People& p)" << endl;
	}
	// p1 = p
	People& operator=(const People& p)
	{
		cout << "People& operator=(const People& p)" << endl;
		return *this;
	}

private:
	char name[20];
	char address[20];
	char tele[20];
};

class Student :public People
{
public:
//无写构造
private:
	int id;
};

int main()
{
	Student t;
	return 0;
}

在没有写派生类的构造函数时,派生类会在编译器生成的默认构造函数当中在初始化列表处调用父类的构造函数对父类的资源进行初始化。

当我们写了子类的构造函数,但是没有显示调用父类的构造函数,编译器依旧会在初始化列表处帮我们调用父类的构造函数对父类的资源进行初始化。

C++规定了派生类要先对父类资源进行初始化,所以不管我们有没有显示调用父类的构造函数,编译器都会帮我们调用。下面展示一下如何显示调用

	Student()
    	:People()
	{
		cout << "Student()" << endl;
	}

倘若父类没有写默认的构造函数,这个时候只能用显示调用的方法对父类的资源初始化了。

调用方法看起来有点奇怪,用起来有点像创建匿名对象,但是便于理解,我们可以把他理解成子类当中将父类看做自定义类型,所以会去默认调用它的构造函数,而我们没有显示写出父类的对象,所以初始化父类的形式用的是类名+(参数...)

4.2拷贝构造

1.当我们没有编写拷贝构造函数的时候,我们发现编译器帮我们默认生成的拷贝构造会自动调用父类的拷贝构造。

那么我们是否跟构造函数一样只拷贝子类的资源即可,编译器是否会帮我们也在初始化列表处对父类资源进行拷贝?
不会

看下面这张图,我们发现拷贝构造当中调用了父类的构造函数

有的同学就会有疑惑了,实际上拷贝构造也是构造,在初始化列表处,对于子类而言,父类相当于一个自定义类型对象,子类会调用父类的构造函数对父类的资源进行初始化。

解决方法:显示调用父类的拷贝构造即可,所以拷贝构造这里我们一定要要写就一定要显示调用父类的拷贝构造

Student(const Student& s)
		:People(s)
	{
		cout << "Student(const Student& s)" << endl;
	}

4.3赋值重载

老样子,先看看编译器生成的默认的operator=是怎样的。

很显然,编译器会自动调用父类的operator=对父类的部分进行赋值,赋值重载与拷贝构造一样,需要我们显示调用父类的赋值重载,否则虽然不会报错,但是不满足我们所需要的行为。

所以我们在函数体内调用父类oeperator=即可:

五、菱形继承和菱形虚拟继承

单/多继承的定义:

单继承:一种继承机制。其中每个子类只能继承单一的超类。

多继承:多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。

多继承本身并没有问题,但是它的扩展形成菱形继承出现了问题,让我们学习的过程中需要学习更加复杂的解决方案。

5.1菱形继承

以下面这张图为例。

struct Base {
	int base;
};

struct A :public Base
{
	int a;
};

struct C :public Base
{
	int c;
};

struct D :public A ,public C
{
	int d;
};

在没有虚继承前,对象模型如下图,可以看出base在D有出现了两份,也就是在D所创建的对象当中都会出现二义性和数据冗余的问题!

解决方案

虚继承,在腰部的类继承时添加virtual关键字。

struct Base {
	int base;
};

struct A :virtual public Base
{
	int a;
};

struct C :virtual public Base
{
	int c;
};

struct D :public A ,public C
{
	int d;
};

int main()
{
	D d;
	d.c = 1;
	d.d = 2;
	d.a = 3;
	return 0;
}

测试平台:vs2013/32位

虚继承后的内存对象成员模型,其中每个腰部虚继承的A,C的对象都多了一个指针,由于我们是小端机,所以对应过去我们能看到指向的空间当中对应8字节,表中头4字节00 00 00 00与多态有关,下面的则是偏移量,由于虚继承后只有一份A对象的成员变量,并且表结构需要8字节的空间,所以A,C对象当中存放的是虚基表指针,指向的是虚基表,虚基表一般是放在代码段 当中的。

为什么C,A需要去找属于自己的Base?

基类与派生类的赋值转换时,需要进行切片,需要将A,C当中的base变量才能赋值给b。

int main()
{
	Base b = A();
	Base b2 = C();
	return 0;
}

上述图中B是否虚继承都可以,只要A,C虚继承,D中都不会出现二义性了。

六、继承的总结

在继承这块实际上是c++语法复杂的一处体现了,有了多继承,就有了菱形继承,相对应他的解决方案来了,但是我们可以发现这套解决方案让他的底层实现必定变得复杂了起来,所以正常使用的时候我们并不推荐去折腾菱形继承,在java等语言都把多继承这一块砍掉了,使用多继承的同时就要考虑复杂度和性能上的问题。

继承和组合

继承是一种复用的方式,但不是唯一方式!

  • public继承是一种is-a的关系,每一个派生类都是一个基类对象。
  • 组合是一种has-a的关系,假设B组合了A,则每个B对象都有一个A对象。
  • 继承方式的复用常称之为白箱复用,在继承方式中,基类的内部细节对子类可见,这一定程度上破坏了基类的封装,伴随着基类的改变,对派生类的改变很大。并且两者依赖关系强,耦合度大。
  • 对象组合式继承之外的复用选择,对象组合要求被组合对象提供良好的接口定义。这种复用称之为黑箱复用,对象的内部实现细节是不可见的。耦合度低。

实际工程中能用继承和组合就用组合,组合的耦合度低,代码的维护性好,但是继承在有些关系就适合用继承就用继承,并且要实现多态就一定要用继承。

总结

继承作为c++的一块难点,本篇博客不免有些错误,欢迎各位大佬指出批评!

到此这篇关于关于C++继承你可能会忽视的点的文章就介绍到这了,更多相关C++继承忽视的点内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 关于C++多重继承下虚表结构的问题

    前言 昨天写一个项目时遇到一个很诡异的现象 如下代码所示: class ParentA { public : int a=0x123456; virtual void vFunParentA(){ std::cout << "vFunParentA" << std::endl; } }; class ParentB { public: int b = 0x456; virtual void vFunParentB() { std::cout<<&qu

  • C++继承详细介绍

    在我们进行开发的时候,我们经常会遇到抽象出来的类之间具有继承关系. 举个简单的例子,比如我们在设计某游戏,当中需要定义Human也就是人这个类.每个人有名字,以及一定的血量,能够工作.也就是说Human这个类具有名字和血量这两个成员变量,还有一个工作的函数. 现在我们还需要开发一个英雄Hero类,英雄也是人,他应该也有名字和血量,以及也可以工作.但英雄又和普通人不同,他具有一些特殊的属性.比如变异,比如超能力等等.那么我们在开发Hero这个类的时候,绝大多数的功能都和Human一样,但是又需要额

  • C++ 继承,虚继承(内存结构)详解

    目录 普通的公有继承 多重继承 虚继承 虚继承(菱形继承) 总结 普通的公有继承 class test1 { public: test1(int i) :num1(i) {} private: int num1; }; class test2 : public test1 { public: test2(int i,int j) : test1(i), num2(j) { } private: int num2; }; void main() { test2 t2(1,2); } (test2内

  • C++继承和动态内存分配

    目录 1.简介 2.派生类不用new 3.派生类使用new 文章转自微信 公众号:Coder梁(ID:Coder_LT) 1.简介 这里面有一个问题,当我们的基类使用动态内存分配,并且重新定义赋值和复制构造函数,这会对派生类的实现有什么影响呢? 我们来看两种情况: 2.派生类不用new 假设基类中使用了动态内存分配: class baseDMA {  private:      char *label;      int rating;     public:      baseDMA(cons

  • C++中菱形继承的解释与处理详解

    封装,继承,多态.这是C++语言的三大特性,而每次在谈到继承时我们不可避免的要谈到一个很重要的问题——菱形继承. 派生类继承父类,同时也会继承父类中的所有成员副本,但如果在继承时一个基类同时被两个子类继承,然后一个新类又分别由上面的两个子类派生出来.这样从某种程度来说就形成了C++中的菱形继承,也可以叫做钻石继承,具体的继承形式如下图所示: 在上面的类图说,Left和Right分别派生子Top,但是Bottom又分别继承了Left和Right.继承关系也可以画成下面的方式,这样就可以更好的理解设

  • C++数据结构继承的概念与菱形继承及虚拟继承和组合

    目录 继承的概念 继承的定义 基类和派生类对象之间的赋值转换 继承中的作用域 派生类的默认成员函数 继承中的两个小细节

  • C/C++中多重继承详解及其作用介绍

    目录 概述 优缺点 优点 缺点 声明多重继承的方法 格式 例子 二义性 两个基类有同名成员 基类和派生类有同名成员 两个基类从同一个基类派生 概述 多重继承 (multiple inheritance): 一个派生类有两个或多个基类, 派生类从两个或多个基类中继承所需的属性. C++ 为了适应这种情况, 允许一个派生类同时继承多个基类. 这种行为称为多重继承. 优缺点 优点 自然地做到了对单继承的扩展 可以继承多个类的功能 缺点 结构复杂化 优先顺序模糊 功能冲突 声明多重继承的方法 格式 多重

  • C++继承模式详解

    目录 继承 继承的概念 继承的定义 继承关系和访限定符 继承方式 父类和子类对象赋值转化 继承中的作用域 子类的默认成员函数 继承与友元 继承与静态成员 复杂的菱形继承 虚继承 继承的总结 继承 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程. 继承的定义 上面的基类也可以叫父类,派生类也可以叫子类. 继承关系和访限定符 继承方式 接下来用代码测试上面的

  • 一篇文章带你了解C++面向对象编程--继承

    目录 C++ 面向对象编程 -- 继承 总结 C++ 面向对象编程 -- 继承 "Shape" 基类 class Shape { public: Shape() { // 构造函数 cout << "Shape -> Constructor" << endl; } ~Shape() { // 析构函数 cout << "Shape -> Destructor" << endl; } vo

  • 关于C++继承你可能会忽视的点

    目录 前言 一.什么是继承 二.基类与派生类的赋值转换 2.1天然支持的理解 三.继承当中的作用域 四.派生类的默认构造成员函数 4.0什么时候需要写6个默认成员函数 4.1构造函数 4.2拷贝构造 4.3赋值重载 五.菱形继承和菱形虚拟继承 5.1菱形继承 六.继承的总结 继承和组合 总结 前言 继承是使代码复用的一种重要的手段,我们在C语言时期写的swap函数逻辑,通常会单独写出来再给其他函数复用,这个继承可以理解成是类级别的一个复用,它允许我们在原有类的基础上进行扩展,增加新的功能. 一.

  • php打开远程文件的方法和风险及解决方法

    PHP有一个配置选项叫allow_url_fopen,该选项默认是有效的.它允许你指向许多类型的资源,并像本地文件一样处理.例如,通过读取URL你可以取得某一个页面的内容(HTML),看下面的代码 复制代码 代码如下: <?php$contents = file_get_contents('http://www.jb51.net/');?> 当被污染数据用于include和require的文件指向时,会产生严重漏洞.实际上,我认为这种漏洞是PHP应用中最危险的漏洞之一,这是因为它允许攻击者执行

  • 老生常谈Java String字符串(必看篇)

    Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "hello";,另一种就是使用new这种标准的构造对象的方法,如String str = new String("hello"); 对于这样的常识,不再赘述. 首先String类是final类,为什么定义成final形式呢? 简单点说,对于如此高频率被使用的数据类型,设计者们认为已经设计的足够优秀了,不需要被继承,否则胡乱继承重写可能会降低程序的性能. 正如标题所述,既然深入,那我们

  • JavaScript中的高级函数

    在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙猴子呀!当你运用好函数时,它能助你取西经,让代码变得优雅简洁,运用不好时,那就遭殃了,要大闹天宫咯~ 除了函数相关的基础知识外,掌握一些高级函数并应用起来,不仅能让JS代码看起来更为精简,还可以提升性能.以下是小编总结的一些常用的.重要的高级函数,加上了一些个人见解,特此记录下来.如果您是JS初学者

  • 浅谈python之新式类

    前言 本文中代码运行的python版本一律采取2.7.13 科普: 经典类:classic class 新式类:new-style class python2.2 之前并没有新式类 python2.2-2.7 新式类与经典类并存, 默认使用经典类, 除非显式继承object python3.X 中去除了经典类, 用户定义的所有类都隐式继承自object  如何使用新式类 class New(object): # 显式继承object类 pass class Old: pass class Old

  • 分享Python 加速运行技巧

    目录 1.避免全局变量 2.避免 2.1 避免模块和函数属性访问 2.2 避免类内属性访问 3.避免不必要的抽象 4,避免数据复制 4.1 避免无意义的数据复制 4.2 交换值时不使用中间变量 4.3 字符串拼接用join而不是+ 5.利用 if 条件的短路特性 6.循环优化 6.1 用for循环代替while循环 6.2 使用隐式for循环代替显式for循环 6.3 减少内层for循环的计算 7.使用 numba.jit 8.选择合适的数据结构 前言: Python 是一种脚本语言,相比 C/

  • AngularJS深入探讨scope,继承结构,事件系统和生命周期

    本文实例讲述了AngularJS的scope,继承结构,事件系统和生命周期.分享给大家供大家参考,具体如下: 深入探讨 Scope 作用域 每一个 $scope 都是类 Scope 的一个实例.类 Scope 拥有可以控制 scope 生命周期的方法,提供事件传播的能力,并支持模板渲染. 作用域的层次结构 让我们再来看看这个简单的 HelloCtrl 的例子: var HelloCtrl = function($scope){ $scope.name = 'World'; } HelloCtrl

  • PostgreSQL教程(三):表的继承和分区表详解

    一.表的继承: 这个概念对于很多已经熟悉其他数据库编程的开发人员而言会多少有些陌生,然而它的实现方式和设计原理却是简单易懂,现在就让我们从一个简单的例子开始吧.     1. 第一个继承表:   复制代码 代码如下: CREATE TABLE cities (   --父表         name        text,         population float,         altitude     int     );     CREATE TABLE capitals (

  • JavaScript 继承机制的实现(待续)

    1.对象冒充 原理:构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式). 因为构造函数只是一个函数,所以可使ClassA的构造函数成为ClassB的方法,然后调用它.ClassB就会收到ClassA的构造函数中定义的属性和方法. 例如: 下面方式定义的ClassA和ClassB: 复制代码 代码如下: function ClassA(sColor){ this.color=sColor; this.sayColor=function(){ alert(this.colo

  • javascript 面向对象全新理练之原型继承

    首先创建一个父类的实例化对象,然后将该对象赋给子类的 prototype 属性. 这样,父类中的所有公有实例成员都会被子类继承.并且用 instanceof 运算符判断时,子类的实例化对象既属于子类,也属于父类. 然后将子类本身赋值给它的 prototype 的 constructor 属性.(注意:这里赋值的时候是没有 () 的!) 这一步是为了保证在查看子类的实例化对象的 constructor 属性时,看到的是子类的定义,而不是其父类的定义. 接下来,通过对 o.method1() 调用的

随机推荐