我想创建一个定义类的某些方法的抽象类。其中一些应该由基类(Base)实现,一些应该在Base中定义但是由Derived覆盖,而其他应该在Base中是纯虚拟的,以强制在Derived中定义。
这当然是抽象类的用途。但是,我的应用程序只会直接使用Derived对象。因此,编译器应该在编译时确切地知道要使用哪些方法。
现在,由于此代码将在内存非常有限的微控制器上运行,因此我非常希望避免实际使用带有vtable的虚拟类。根据我的测试,似乎编译器足够聪明,除非必须,否则不能制作vtable,至少在某些情况下。但是我被告知永远不要相信编译器:是否有可能使这成为编译的必要条件?
以下是一些代码示例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Registration Form</title>
<link href="style/bootstrap-3.3.7.min.css" rel="stylesheet" />
<script src="script/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css " />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript ">
var limit = 10;
var Name;
var FatherName;
var CellNo;
$(document).ready(function () {
$("#btnSubmit").click(function () {
$('#txtName').val();
if (name == "") {
alert("please enter the name");
return ;
}
});
$('#txtFatherName').val();
if (FatherName == "") {
alert("please enter ur father name");
return;
}
$('#txtEmail').val();
if (Email == "") {
alert("please enter email");
return;
}
$('#txtAddress').val();
if (Address == "") {
alert("please enter email");
return;
}
$('#txtCellNo').val();
if (CellNo == "") {
alert("please enter email");
return;
}
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div id="personalInfo">
<div>
<div class="container">
<h1>Personal Information</h1>
</div>
<br />
<div class="row">
<div class="col-sm-2 text-right">Name :</div>
<div class="col-sm-4">
<input type="text" id="txtName" class="cls1" name="txtName" size="20" />
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 text-right">Father Name :</div>
<div class="col-sm-4">
<input type="text" id="txtFatherName" class="cls1" name="txtFatherName" size="20" />
</div>
</div>
<br />
</div>
<div class=" row">
<div class="col-sm-2 text-right">Email:</div>
<div class="col-sm-4">
<input type="text" id="txtEmail " class="cls1" name="txtEmail" size="20" />
</div>
</div>
<div>
<br />
<div class="row">
<div class="col-sm-2 text-right">Address</div>
<div class="col-sm-4">
<input type="text" id="txtAddress" class="cls1" name="txtAddress" size="20" />
</div>
</div>
<br />
<div class="row">
<div class="col-sm-2 text-right">Cell No:</div>
<div class="col-sm-4">
<input type="text" id="txtCellNo" class="cls1" name="txtCellNo" size="20" />
</div>
</div>
<br />
<div class=" row">
<div class=" col-sm-2 text-right"></div>
<div class="col-sm-2">
<button type="button" id="btnSubmit" class="btn btn-primary">Submit</button>
<button type="button" id="btnCancel" class="btn btn-default">Cancel</button>
</div>
</div>
</div>
</div>
</form>
</body>
</html>
这没有vtable,是我想要的
class Base {
public:
Base() {}
virtual ~Base() {};
virtual int thisMustBeDefined() = 0;
virtual int thisCouldBeOverwritten() { return 10; }
int thisWillBeUsedAsIs() { return 999; }
};
class Derived : public Base {
public:
Derived() {}
~Derived() {}
int thisMustBeDefined() { return 11; }
};
由于我的草率编码,我错误地强迫编译器使用多态,因此需要一个vtable。如何让这个案例抛出错误?
int main() {
Derived d;
d.thisMustBeDefined();
}
这里我没有提到班级&#34; Base&#34;在任何时候,编译器都应该知道所有方法都是在编译时预先确定的。然而,它仍然创造了一个vtable。这是我希望能够通过编译错误检测到这一点的另一个例子。
int main() {
Base * d;
d = new Derived();
d->thisMustBeDefined();
}
换句话说,如果我编写导致编译器为我的类生成vtable的代码,即使用多态,我希望它是编译器错误。
答案 0 :(得分:6)
正如评论中已经提到的那样,你可以使用CRTP(又名静态多态)来避免创建vtable:
template <typename Der>
class Base {
public:
Base() {}
~Base() {};
int thisMustBeDefined() {
// Will fail to compile if not declared in Der
static_cast<Der*>(this)->thisMustBeDefined();
}
int thisCouldBeOverwritten() { return 10; }
int thisWillBeUsedAsIs() { return 999; }
};
class Derived : public Base<Derived> {
public:
Derived() {}
~Derived() {}
int thisMustBeDefined() { return 11; }
// Works since you call Derived directly from main()
int thisCouldBeOverwritten() { return 20; }
};
如果Derived
中没有实现某个函数,可以使编译器错误更具可读性,您可以使用this answer中提供的简单静态检查:
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
template<typename T, T> struct helper; \
template<typename T> \
static std::uint8_t check(helper<signature, &funcName>*); \
template<typename T> static std::uint16_t check(...); \
public: \
static \
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
}
DEFINE_HAS_SIGNATURE(thisMustBeDefined, T::thisMustBeDefined, int(*)(void));
并将静态检查添加到Base
构造函数:
Base() {
static_assert(thisMustBeDefined<Der>::thisMustBeDefined,
"Derived class must implement thisMustBeDefined");
}
虽然在使用小型设备时应考虑一个缺点,并且您一次拥有更多版本的Derived
,但每个Base
的{{1}}代码都会重复实例
因此,您必须确定对您的用例更重要的限制是什么。
正如@ChrisDrew在comment中指出的那样,将Derived
和thisCouldBeOverwritten()
函数移动到thisWillBeUsedAsIs()
模板类派生的另一个基类会促进该问题。