c++面向对象基本
默认构造
抽象类
多继承
本章讲授的主要内容依然围绕C++中的面向对象世界观的构建,其中牵涉到构造函数、抽象类与多重继承的概念。
默认构造
编译器生成的构造函数,被称为默认构造函数,该构造函数不带参数。
C++中类的构造分为不带参的构造函数与带参构造函数,带参构造函数需要用户自己定义并实现,而不带参的构造函数并不一定需要用户自己实现,在『某些』情况下,编译器会为类生成默认的无参构造函数。
也就是说,若没有手动实现类的构造函数,那么该类有可能存在默认的构造函数,有可能没有默认的构造函数。
那么,编译器是否生成默认构造函数的原则有哪些呢?
总的原则:若用户手动实现了构造函数,无论其是否带参,编译器都不再生成默认构造函数
编译器是倾向于不给用户默认实现构造函数的,但是,当满足以下3条的任意一条情况时,编译器会生成默认构造函数
1、父类已经实现不带参构造函数
2、类中存在虚函数
3、类中存在对象成员,且该成员存在无参构造(无论是自定义还是默认)
#include <iostream>
using namespace std;
class Node{
public:
Node(int v):value(v){}
private:
int value;
};
int main(){
//Node node;编译报错,因为Node类自定义了构造函数,编译器不会再生成默认的构造函数,因此无法通过不带参构造函数创建对象(因为Node并没有不带参的构造函数)
return 0 ;
}
父类已经实现不带参构造函数时,当子类没有实现构造函数式,编译器会生成默认构造函数
#include <iostream>
using namespace std;
class Node{
public:
Node(){}
};
class TTNode:public Node{
};
int main(){
TTNode ttnode;//可以使用不带参构造函数,尽管用户没有实现不带参的构造函数,但是编译器生成了默认构造函数
return 0;
}
父类已经实现带参构造函数时,无论如何子类都不会生成默认构造函数
#include <iostream>
using namespace std;
class Node{
public:
Node(int v){}
};
class TTNode:public Node{
};
int main(){
//TTNode ttnode;编译报错,父类实现了带参构造函数后,编译器并不会给子类生成默认构造函数
return 0;
}
当类中存在虚函数时,无论虚函数是通过继承父类而来还是原本就定义在本类中,编译器都会尝试生成默认构造函数
#include <iostream>
using namespace std;
class Node{
public:
virtual void showValue(){}
};
int main(){
Node node;//类中定义了虚函数,但是又没有现实构造函数,编译器会为Node类生成默认构造函数
return 0;
}
#include <iostream>
#include "Node.h"
using namespace std;
class TTNode:public Node{
public:
void showValue(){}
};
int main(){
TTNode ttnode;//同样会生成默认的构造函数,从父类继承了虚函数
return 0;
}
当类中存其他类的成员对象时,编译器也会为本类生成默认构造函数
#include <iostream>
using namespace std;
class Node{
};
class TTNode{
public:
Node node;
};
int main(){
TTNode ttnode;//编译器会为TTNode创建默认的构造函数,因为其成员对象Node类存在无参构造函数(无论其是否是编译器生成还是自定义)
return 0;
}
抽象类、纯虚函数
抽象类的本质是定义接口
面向接口编程也是一种区别于面向对象编程的一种编程范式。当然,接口本身也在面向对象编程中有大量运用,既能规范接口调用又能增加灵活性
所谓抽象类,就是类中存在纯虚函数的类
#include <iostream>
using namespace std;
class NodeInterface{
public:
//通过特殊语法定义纯虚函数
virtual void showvalue() = 0;
};
class TTNode:public NodeInterface{
public:
void showvalue(){
cout<<"ttnode show value"<<endl;
}
};
int main(){
//NodeInterface nodeinterface;编译报错,含有纯虚函数的类被称为抽象类。抽象类无法创建对象,仅作为接口规范存在
TTNode ttnode;
ttnode.showvalue();//ttnode show value
NodeInterface *node = new Node;
node->showvalue();//ttnode show value
//子类必须重写其所继承的抽象类的所有纯虚函数,否则,子类本身也会称为抽象类,无法被实例化
return 0;
}
纯虚函数没有自己的实现,只是提供函数签名与接口,所有的实现都交由其子类,而子类又必须实现其父类的纯虚函数
既然是接口,那么就可以有多个接口,并且一个类可以同时实现多个接口,这就涉及到多重继承的问题
#include <iostream>
using namespace std;
class NodeInterface{
public:
virtual void showNodeValue() = 0;
};
class NodeActionInterface{
public:
virtual void doAction() = 0;
};
class NodeItem:public NodeInterface,public NodeActionInterface{
public:
void showNodeValue(){
//
}
void doAction(){
//
}
};
int main(){
NodeItem item;
item.showNodeValue();
item.doAction();
return 0;
}
一个类可以同时继承多个父类,这就是多重继承。多重继承的最常用场合就是继承虚基类,实现虚函数接口。
当然,也可以从面相对象编程的角度上去多重继承,但是这种方式极不推荐,不仅存在各种隐藏的问题,同时也会使得类与类之间存在混乱的关系。
#include <iostream>
using namespace std;
class A{
public:
int value;
};
class B:public A{};
class C:public A{};
class D:public B,public C{};
int main(){
//这就是多重继承中非常容易出现的『菱形继承』,他的问题在于当类B类C同时存在同名的成员名以及同名的成员函数时,子类D该用哪个父类的成员函数与成员,这样就出现了二义性,这是多重继承无法避免的问题。
//为了解决这个问题,C++提出了『虚继承』的概念,专门用来解决多重继承中可能出现的这个问题
D d;
//d.value = 11;编译报错
return 0;
}
用虚继承解决承菱形继承中可能出现的问题
#include <iostream>
using namespace std;
class A{
public:
int value;
};
class B:virtual public A{};//虚继承
class C:virtual public A{};
class D:public B,public C{};
int main(){
//使用虚继承解决菱形继承中成员的二义性问题
D d;
d.value = 11;
return 0;
}
此外,多重继承与普通继承在内存机制上并没有不同。
最先构造的是祖父A,然后是父类B,然后是父类C,最后是自己D。
多个父类的构造顺序取决于源码中的继承顺序(从左往右)
关于父类指针与子类指针的互转问题
父类指针指向子类对象,无疑是安全的。
因为父类指针的接口能够访问到的成员范围小于等于子类,因此无论如何都不会越界。
而当子类指针指向父类时,则有可能存在访问越界的风险,因为子类的成员范围有可能大于父类,因此当通过子类指针接口进行访问时,极有可能导致过度偏移造成访问越界。
#include <iostream>
using namespace std;
class Node{
public:
int value;
};
class TTNode:public Node{
public:
int tid;
};
int main(){
Node *pnode = new TTNode;
pnode->value = 11;//安全的,通过Node指针能够访问到的对象只有value,其偏移为0
//不安全的,TTNode能访问的范围比Node大,当通过pnode对象访问成员tid时,偏移为4,此时已经超出了Node对象的内存范围,越界访问导致出错
TTNode *pnode = (TTNode*)new Node;//WARING
return 0;
}
总之,不要尝试使用子类指针访问父类对象,除非你明确知道自己的行为
posted on 2021-12-28 00:23 shadow_fan 阅读(23) 评论(0) 编辑 收藏 举报