C++11:引用折叠,完美转发,可变参数模板,defult和delete,final和override,委托构造函数,继承构造函数
一、引用折叠
• C++中不能直接定义引用的引用如 int& && r = i; ,这样写会直接报错,通过模板或typedef 中的类型操作可以构成引用的引用。
• 通过模板或typedef中的类型操作可以构成引用的引用,这时C++11给出了一个引用折叠的规则:右值引用的右值引用折叠成右值引用,所有其他组合均折叠成左值引用。
• 下面的程序中很好的展示了模板和typedef时构成引用的引用时的引用折叠规则。
// 由于引用折叠限定,f1实例化以后总是一个左值引用
template<class T>
void f1(T& x)
{}// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用,所以被叫万能引用
template<class T>
void f2(T&& x)
{}int main()
{typedef int& lref;typedef int&& rref; // 或者这样 using rref = int&&;int n = 0;lref& r1 = n; // r1 的类型是 int&lref&& r2 = n; // r2 的类型是 int&rref& r3 = n; // r3 的类型是 int&rref&& r4 = 1; // r4 的类型是 int&&// 没有折叠->实例化为void f1(int& x)f1<int>(n);//f1<int>(0); // 报错,不能传右值// 折叠->实例化为void f1(int& x)f1<int&>(n);//f1<int&>(0); // 报错,不能传右值// 折叠->实例化为void f1(int& x)f1<int&&>(n);//f1<int&&>(0); // 报错,不能传右值// 折叠->实例化为void f1(const int& x)f1<const int&>(n);f1<const int&>(0);// 折叠->实例化为void f1(const int& x)f1<const int&&>(n);f1<const int&&>(0);// 没有折叠->实例化为void f2(int&& x)//f2<int>(n); // 报错f2<int>(0);// 折叠->实例化为void f2(int& x)f2<int&>(n);//f2<int&>(0); // 报错// 折叠->实例化为void f2(int&& x)//f2<int&&>(n); // 报错,这里需要右值f2<int&&>(0);return 0;
}
// 万能引用,既可以实例化出左值引用的版本,也可以实例化右值引用的版本
// 传左值就实例化左值引用的函数,传右值就实例化右值引用的函数
template<class T>
void Function(T&& t)
{int a = 0;T x = a;//x++;cout << &a << endl;cout << &x << endl << endl;
}int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)Function(a);// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// a是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int& t)// 所以Function内部会编译报错,x不能++Function(b);// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)// 所以Function内部会编译报错,x不能++Function(std::move(b)); // const 右值return 0;
}• 像f2这样的函数模板中,T&& x参数看起来是右值引用参数,但是由于引用折叠的规则,它传递左值时就是左值引用,传递右值时就是右值引用,有些地方也把这种函数模板的参数叫做万能引用。
• Function(T&&t)函数模板程序中,假设实参是int右值,模板参数T的推导int,实参是int左值,模 板参数T的推导int&,再结合引用折叠规则,就实现了实参是左值,实例化出左值引用版本形参的 Function,实参是右值,实例化出右值引用版本形参的Function。
二、完美转发
• Function(T&& t)函数模板程序中,传左值实例化以后是左值引用的Function函数,传右值实例化 以后是右值引用的Function函数。
• 变量表达式都是左值属性,也就意味着一个右值被右值引用绑定 后,右值引用变量表达式的属性是左值,也就是说Function函数中t的属性是左值,那么我们把t传递给下⼀层函数Fun,那么匹配的都是左值引用版本的Fun函数。这里我们想要保持t对象的属性, 就需要使用完美转发实现。
template T&& forward (typename remove_reference::type& arg);template T&& forward (typename remove_reference::type&& arg) ;• 完美转发forward本质是一个函数模板,他主要还是通过引用折叠的方式实现,下面示例中传递给 Function 的实参是右值,T被推导为int,没有折叠,forward内部t被强转为右值引用返回;传递给 Function 的实参是左值,T被推导为int&,引用折叠为左值引用,forward内部t被强转为左值引用返回。
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }// 万能引用
template<class T>
void Function(T&& t)
{//Fun(t);Fun(forward<T>(t));
}
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// a是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int& t)Function(b);// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&& t)Function(std::move(b)); // const 右值return 0;
}三、可变参数模板
1.基本语法及原理
• C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函数参数。
template<class ...Args> void Func(Args... args) {} template<class ...Args> void Func(Args&... args) {} template<class ...Args> void Func(Args&&... args) {}• 用省略号来指出一个模板参数或函数参数的表示一个包,在模板参数列表中,class... 或 typename... 指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟... 指出 接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示,跟前面普通模板 一样,每个参数实例化时遵循引用折叠规则。
• 可变参数模板的原理和模板类似,本质还是去实例化对应类型和个数的多个函数。
#include <iostream>
#include <string>
using namespace std;template <class ...Args>
void Print(Args&&... args)
{cout << sizeof...(args) << endl; // 用于计算出包里面的变量个数
}int main()
{double x = 2.2;Print(); // 包里有0个参数Print(1); // 包里有1个参数Print(1, string("xxxxx")); // 包里有2个参数Print(1.1, string("xxxxx"), x); // 包里有3个参数return 0;
}2.包扩展
对于一个参数包,我们除了能计算他的参数个数,我们能做的唯一的事情就是扩展它,当扩展一个包时,我们还要提供用于每个扩展元素的模式,扩展一个包就是将它分解为构成的元素,对每个元素应用,获得扩展后的列表。我们通过在模板的右边放一个省略号(...)来触发扩展操作。C++还支持更复杂的包扩展,直接将参数包依次展开依次作为实参给一个函数去处理。
void showList()
{cout << endl;
}template<class T, class ...Args>
void showList(T x, Args... args)
{cout << x << " ";showList(args...);
}
template<class ...Args>
void Print(Args... args)
{showList(args...);
}int main()
{double x = 2.2;Print(); // 包里有0个参数Print(1); // 包里有1个参数Print(1, string("xxxxx")); // 包里有2个参数Print(1.1, string("xxxxx"), x); // 包里有3个参数return 0;
}// 本质编译器将可变参数模板通过模式的包扩展,编译器推导的以下三个重载函数函数
//void ShowList(double x)
//{
// cout << x << " ";
// showList();
// }
//
//void ShowList(string x, double z)
//{
// cout << x << " ";
// showList(z);
//}
//void ShowList(int x, string y, double z)
//{
// cout << x << " ";
// showList(y, z);
//}
//void Print(int x, string y, double z)
//{
// ShowList(x, y, z);
//}这里看着非常像函数的递归,有人会问:为什么出口不写成和函数递归出口一样的方式呢?回答:这里涉及模板元编程,即这里的Print函数是在程序编译时就展开的,不是在程序运行时展开的
template <class T>
const T& GetArg(const T& x)
{cout << x << " ";return x;
}template <class ...Args>
void Arguments(Args... args)
{}template <class ...Args>
void Print(Args... args)
{// 注意GetArg必须返回获得到的的对象,这样才能组成参数包给ArgumentsArguments(GetArg(args)...);
}int main()
{double x = 2.2;Print(); // 包里有0个参数Print(1); // 包里有1个参数Print(1, string("xxxxx")); // 包里有2个参数Print(1.1, string("xxxxx"), x); // 包里有3个参数return 0;
}3.empalce系列接口
template void emplace_back (Args&&... args);template iterator emplace (const_iterator position, Args&&... args);• C++11以后STL容器新增了empalce系列的接口,empalce系列的接口均为模板可变参数,功能上 兼容push和insert系列,但是empalce还支持新玩法,假设容器为container,empalce还支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。
• emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列
• List.h程序中模拟实现了list的emplace和emplace_back接口,这里把参数包不段往下传递, 最终在结点的构造中直接去匹配容器存储的数据类型T的构造,所以达到了前面说的empalce支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。
• 传递参数包过程中,如果是 Args&&... args 的参数包,要用完美转发参数包,方式如下 std::forward<Args>(args)... ,否则编译时包扩展后右值引用变量表达式就变成了左值。
// List.h
#pragma once#include<assert.h>
namespace mystl
{template<class T>struct list_node{list_node* _next;list_node* _prev;T _data;list_node(const T& x):_next(nullptr), _prev(nullptr), _data(x){}list_node(T&& x = T()):_next(nullptr), _prev(nullptr), _data(move(x)){}template <class ...Args>list_node(Args&&... args): _next(nullptr), _prev(nullptr), _data(forward<Args>(args)...){}};// typedef list_iterator<T, T&, T*> iterator;// typedef list_iterator<T, const T&, const T*> const_iterator;template<class T, class Ref, class Ptr>struct list_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}};template<class T>class list{typedef list_node<T> Node;public:typedef list_iterator<T, T&, T*> iterator;//typedef list_const_iterator<T> const_iterator;typedef list_iterator<T, const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}list(initializer_list<T> il){empty_init();for (auto& e : il){push_back(e);}}// lt2(lt1)//list(const list<T>& lt)list(list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}// lt1 = lt3list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){auto it = begin();while (it != end()){it = erase(it);}}size_t size(){return _size;}void push_back(const T& x){/*Node* tail = _head->_prev;Node* newnode = new Node(x);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}// 不是万能引用// T是通过实参传递推导的吗?不是,T是list实例化的void push_back(T&& x){//insert(end(), move(x));insert(end(), (T&&)x);}// 万能引用//template<class X>//void push_back(X&& x)//{// // 保持他的属性,往下传递,左值引用就是保持左值属性,右值引用就保持右值属性// insert(end(), forward<X>(x));//}//template<class X>//void insert(iterator pos, X&& x)//{// Node* cur = pos._node;// Node* prev = cur->_prev;// Node* newnode = new Node(forward<X>(x));// // prev newnode cur// prev->_next = newnode;// newnode->_prev = prev;// newnode->_next = cur;// cur->_prev = newnode;// ++_size;//}template <class... Args>void emplace_back(Args&&... args){emplace(end(), forward<Args>(args)...);}template <class... Args>void emplace(iterator pos, Args&&... args){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(forward<Args>(args)...);// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}void insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;}void insert(iterator pos, T&& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(move(x));// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;}iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;//return iterator(next);return next;}private:Node* _head;size_t _size;};
}
// test.c
#include<list>
#include"List.h"int main()
{mystl::list<pair<string, int>> lt1;// 跟push_back一样// 构造pair + 拷贝/移动构造pair到list的节点中data上pair<string, int> kv("苹果", 1);lt1.emplace_back(kv);cout << "*********************************" << endl;// 跟push_back一样lt1.emplace_back(move(kv));cout << "*********************************" << endl;////////////////////////////////////////////////////////////////////// 直接把构造pair参数包往下传,直接用pair参数包构造pair// 这里达到的效果是push_back做不到的lt1.emplace_back("苹果", 1);// lt1.emplace_back({ "苹果", 1 }); 不支持,形参是模板,无法推导形参类型lt1.push_back({ "苹果", 1 }); // 隐式类型转换传参cout << "*********************************" << endl;return 0;
}四、默认的移动构造和移动赋值
• 原来C++类中,有6个默认成员函数:构造函数/析构函数/拷贝构造函数/拷贝赋值重载/取地址重载/const取地址重载,最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的。C++11新增了两个默认成员函数,移动构造函数和移动赋值运算符重载。
• 如果没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
• 如果没有自己实现移动赋值重载函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
• 如果提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值
class Person
{
public:Person(const char* name = "张三", int age = 10):_name(name), _age(age){}//~Person()//{}private:string _name;int _age = 1;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1); // 编译器为Person默认生成的移动构造Person s4;s4 = std::move(s2); // 编译器为Person默认生成的移动赋值return 0;
}成员变量声明时给缺省值(C++11的)
五、defult和delete
• C++11为了可以更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
• 如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明而已, 这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
六、final与override
在 C++11 及后续标准中,final 和 override 是两个关键字修饰符,用于增强类继承体系的安全性和可读性,明确开发者对继承行为的意图,避免隐式错误。
override:显式重写基类虚函数
作用
用于子类成员函数,明确表示 “该函数是重写基类的虚函数”。编译器会强制校验:
- 基类中是否存在与该函数签名完全一致(函数名、参数列表、返回值、CV 限定符、引用限定符均匹配)的虚函数;
- 若不匹配(如基类无此虚函数、签名不一致),编译器直接报错,而非隐式定义一个新的成员函数。
核心价值
避免 “意外重载” 或 “重写失败” 的隐式错误(比如手滑写错函数名、参数类型)。
语法规则
- 只能修饰非静态成员函数(静态函数不能被重写);
- 必须放在函数声明的末尾(参数列表之后,const/volatile 或引用限定符之后,函数体之前);
- 重写函数的访问权限可以与基类不同(如基类 protected,子类可 public),但签名必须完全一致(协变返回值除外)。
#include <iostream>
using namespace std;class Base {
public:// 基类虚函数virtual void show(int x) const {cout << "Base: " << x << endl;}virtual Base* clone() const { // 协变返回值:基类返回Base*return new Base();}
};class Derived : public Base {
public:// 正确:显式重写基类 show(),签名完全一致void show(int x) const override { // override 放在 const 之后cout << "Derived: " << x << endl;}// 正确:协变返回值(子类返回Derived*,与基类Base*兼容)Derived* clone() const override {return new Derived();}// 错误1:基类无 void show(double) 虚函数,重写失败// void show(double x) const override;// 错误2:基类 show() 有 const 限定,子类无,签名不一致// void show(int x) override;
};int main() {Base* ptr = new Derived();ptr->show(10); // 输出 Derived: 10(多态生效)delete ptr;return 0;
}final:禁止继承或重写
作用
有两种用法:修饰类 和 修饰虚函数,核心是 “禁止后续扩展”。
1. 修饰类:禁止该类被继承
- 语法:class 类名 final { ... };
- 被 final 修饰的类称为 “最终类”,不能作为基类,子类继承会直接编译报错;
- 最终类中可以有虚函数,但子类无法继承,自然也无法重写。
2. 修饰虚函数:禁止该虚函数被进一步重写
- 语法:virtual 返回值 函数名(参数) final;(final 放在函数声明末尾);
- 被 final 修饰的虚函数,在子类中不能被重写(即使子类想重写,编译器报错);
- 仅禁止 “重写”,不影响 “继承”(子类仍可继承该函数并直接调用)。
语法规则
- final 不能修饰构造函数(构造函数不能被继承 / 重写);
- 修饰虚函数时,该函数必须是基类中的虚函数(非虚函数无需 final,本身就不能被重写);
- final 和 override 不能同时修饰同一个函数(逻辑冲突:override 表示 “要重写”,final 表示 “不能被重写”)。
示例
#include <iostream>
using namespace std;// 用法1:final 修饰类 → 禁止被继承
class FinalClass final {
public:void func() { cout << "FinalClass: 不能被继承" << endl; }
};// 错误:FinalClass 是最终类,无法继承
// class SubClass : public FinalClass {};// 用法2:final 修饰虚函数 → 禁止被重写
class Base {
public:virtual void show() { cout << "Base: show()" << endl; }// 基类中标记 showFinal 为 finalvirtual void showFinal() final { cout << "Base: showFinal()" << endl; }
};class Derived : public Base {
public:// 正确:重写基类的 show()(未被 final 修饰)void show() override { cout << "Derived: show()" << endl; }// 错误:showFinal() 被基类标记为 final,禁止重写// void showFinal() override { cout << "Derived: showFinal()" << endl; }
};class GrandDerived : public Derived {
public:// 正确:重写 Derived 的 show()(基类 show() 未被 final)void show() override { cout << "GrandDerived: show()" << endl; }// 错误:showFinal() 间接继承自 Base,仍被 final 限制// void showFinal() { ... }
};int main() {Base* ptr = new GrandDerived();ptr->show(); // 输出 GrandDerived: show()(多态生效)ptr->showFinal(); // 输出 Base: showFinal()(无法重写,调用基类版本)delete ptr;return 0;
}final 与 override 的核心区别
特性 | override | final |
修饰对象 | 子类的非静态成员函数 | 类 或 基类的虚函数 |
核心作用 | 显式声明 “重写基类虚函数”,编译器校验 | 禁止类被继承 或 虚函数被重写 |
编译校验逻辑 | 检查基类是否有匹配的虚函数 | 检查是否有子类试图继承 / 重写 |
兼容性 | 可与 const/volatile/ 引用限定符共存 | 可与 virtual 共存(修饰虚函数时) |
冲突修饰符 | 不能与 final 同时修饰同一个函数 | 不能与 override 同时修饰同一个函数 |
四、最佳实践
- 重写虚函数时必加 override:即使语法允许省略,显式添加可让代码意图更清晰,编译器帮你规避签名错误;
- 明确禁止继承时用 final 修饰类:比如工具类、无需扩展的基础类,避免不必要的继承;
- 禁止虚函数进一步重写时用 final 修饰函数:比如基类已实现稳定逻辑,不希望子类修改的核心虚函数;
- 注意:final/override 是关键字修饰符,不是函数说明符,必须放在函数声明的末尾(如 const 之后、函数体之前)。
通过合理使用 final 和 override,可以让 C++ 继承体系更健壮、可读性更强,减少因隐式行为导致的 bug。
七、委托构造函数
C++中委托构造函数(Delegateing Constructor)是C++11引入的特性,允许一个构造函数调用同类中的其他构造函数,从而减少代码重复并提高可维护性。
被委托的构造函数必须初始化所有成员变量,因为委托构造函数后不能再重复初始化。
class Example {
public:Example(int a, int b):_x(a),_y(b){cout << "目标构造函数\n";}//Example(int a)// : Example(a, 0)// ,_y(1) // 不能//{// cout << "委托构造函数\n";//}Example(int a): Example(a, 0){cout << "委托构造函数\n";}int _x;int _y;
};class Time {
public:Time(int h, int m):_hour(h), _minute(m){}// error C3511: “Time”: 对委托构造函数的调用应仅为成员初始值设定项// error C2437 : “_second”: 已初始化/*Time(int h, int m, int s):Time(h, m), _second(s){}*/private:int _hour;int _minute;int _second = 0;
};int main()
{Example(1, 2);Example(1);return 0;
}八、继承构造函数
继承构造函数是 C++11引入的一项特性,允许派生类直接继承基类的构造函数,而不需要手动重新定义一个构造函数,这一特性显著简化了派生类的编写,特别是在基类有多个构造函数的情况下。
派生类继承基类的普通构造函数,特殊的拷贝构造/移动构造不继承
继承构造函数中派生类自己的成员变量如果有缺省值会使用缺省值初始化,如果没有缺省值那么和之前类似,内置类型成员不确定,自定义类型成员使用默认构造初始化。
本质:让编译器自己生成派生类对象基类参数的构造
可以直接使用的场景列举:
1.没有成员变量的派生类
2.成员变量都有缺省值,并且我们就想用这个缺省值初始化
class Base {
public:Base(int x, double d):_x(x), _d(d){}Base(int x):_x(x){}Base(double d):_x(d){}protected:int _x = 0;double _d = 0;
};// 传统的派生类实现构造
//class Derived : public Base {
//public:
// Derived(int x) : Base(x) {}
// Derived(double d) : Base(d) {}
// Derived(int x, double d) : Base(x, d) {}
//};
// c++11:
class Derived : public Base {
public:using Base::Base;protected:int _i;string _s;
};int main()
{//Derived d;Derived d1(1);Derived d2(1.1);Derived d3(2, 2.2);return 0;
}