#include
#include
struct dev_info_tag{
int camera_sdk_port= -1; //设备sdk端口
std::string camera_id; //设备标识
};
class dev_abstract {
public:
int camera_port = 0;
std::string camera_id;
public:
explicit dev_abstract(const std::shared_ptr<struct dev_info_tag>& devInfo);
};
dev_abstract::dev_abstract(const std::shared_ptr<struct dev_info_tag> &devInfo) {
camera_id = devInfo->camera_id;
camera_port = devInfo->camera_sdk_port;
}
class dev_dahua: public dev_abstract {
public:
explicit dev_dahua(const std::shared_ptr<struct dev_info_tag>& devInfo);
};
dev_dahua::dev_dahua(const std::shared_ptr<struct dev_info_tag>& devInfo):dev_abstract(devInfo){
}
typedef std::shared_ptr<dev_abstract> dev_abstract_ptr;
using namespace std;
int main(){
std::shared_ptr<struct dev_info_tag> devInfo = make_shared<dev_info_tag>();
devInfo->camera_id = "aaaa";
devInfo->camera_sdk_port = 8888;
dev_abstract_ptr dev = make_shared<dev_dahua>(devInfo);
printf("%s\n", dev->camera_id.c_str());
return 0;
}
virtual在英文中表示“虚”、“虚拟”的含义。c++中的关键字“virtual”主要用在两个方面:虚函数与虚基类。下面将分别从这两个方面对virtual进行介绍。
虚函数源于c++中的类继承,是多态的一种。在c++中,一个基类的指针或者引用可以指向或者引用派生类的对象。同时,派生类可以重写基类中的成员函数。这里“重写”的要求是函数的特征标(包括参数的数目、类型和顺序)以及返回值都必须与基类中的函数一致。如下所示:
class base
{
public:
void test(){ cout<<"基类方法!"<<endl; }
};
class inheriter:public base
{
public:
void test(){ cout<<"派生类方法!"<<endl; } //重写基类方法, 这个方法可以实现也可以不实现。不管实现不实现,都指向父类方法
};
可以在基类中将被重写的成员函数设置为虚函数,其含义是:当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型。
base *p1=new base;
base *p2=new inheriter;
p1->test(); //输出“基类方法”
p2->test(); //输出“基类方法”
#include
#include
using namespace std;
class base
{
public:
virtual void test(){ cout<<"基类方法!"<<endl; }
};
class inheriter:public base
{
public:
void test() override { cout<<"派生类方法!"<<endl; } //重写基类方法: 这个方法可以实现也可以不实现。不实现,指向父类方法,输出“基类方法”;实现,指向派生类方法,输出”派生类方法“
};
int main()
{
base *p1=new base;
base *p2=new inheriter;
p1->test(); //输出“基类方法”
p2->test(); //输出“派生类方法”
return 0;
}
如此,便可以将基类与派生类的同名方法区分开,实现多态。
说明:
#include
#include
using namespace std;
class Shape{
public:
Shape() {
cout<<"Base::Draw()"<<endl;
}
~Shape() {
cout<<"Base::Erase()"<<endl;
}
};
class Polygon:public Shape{
public:
Polygon() {cout<<"Polygon::Draw()"<<endl;}
~Polygon() {cout<<"Polygon Erase()"<<endl;}
};
class Rectangle:public Polygon{
public:
Rectangle() {cout<<"Rectangle::Draw()"<<endl;}
~Rectangle() {cout<<"Rectangle Erase()"<<endl;}
};
在C++的类继承[无论以什么方式继承]中,
建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推;
析构对象时,其顺序正好与构造相反;
int main()
{
Polygon p;
/* Base::Draw()
Polygon::Draw()
Polygon Erase()
Base::Erase()*/
return 0;
}
int main()
{
Polygon *p = new Polygon();
/* Base::Draw()
Polygon::Draw()*/
return 0;
}
int main()
{
auto *p = new Polygon(); //== Polygon *p = new Polygon(); // auto *会自动识别为Polygon
/* Base::Draw()
Polygon::Draw()
*/
delete p;
/*Polygon Erase()
Base::Erase()*/
return 0;
}
int main()
{
Shape *p = new Polygon();
/* Base::Draw()
Polygon::Draw()
*/
delete p; //
/*Base::Erase()*/
return 0;
}
#include
using namespace std;
class Person{
public:
Person(int Age,string Name):m_nAge(Age),m_strName(Name){
cout<<"Person:基类的代参构造函数"<<endl;
}
~Person(){ //
cout<<"Person:基类的析构构造函数"<<endl;
}
private:
int m_nAge;
string m_strName;
};
class Student: protected Person{
public:
Student(int Age,string Name,int Num):Person(Age,Name),m_nNum(Num){
cout<<"Student:子类的代参构造函数"<<endl;
}
~Student(){ //
cout<<"Student:子类的析构构造函数"<<endl;
}
Student(const Student&stu):Person(stu),m_nNum(stu.m_nNum){
cout<<"Student:子类的拷贝构造函数"<<endl;
}
Student& operator= (const Student&stu){ // 子类的拷贝赋值
if(this != &stu)
{
Person::operator= (stu);
m_nNum = stu.m_nNum;
}
return *this;
}
private:
int m_nNum;
};
使用:
int main()
{
Student *student = new Student(11, "aaa", 11);
/*
* Person:基类的代参构造函数
* Student:子类的代参构造函数
* */
return 0;
}
int main()
{
Student *student = new Student(11, "aaa", 11);
delete student;
/*
Person:基类的代参构造函数
Student:子类的代参构造函数
Student:子类的析构构造函数
Person:基类的析构构造函数
* */
return 0;
}
静态绑定和动态绑定是C++多态性的一种特性。
对象的静态类型:对象在声明是采用的类型,在编译期确定;
对象的动态类型:当前对象所指的类型,在运行期决定,对象的动态类型可以更改,但静态类型无法更改。
int main()
{
Polygon *p = new Polygon(); // p的静态类型是它声明的类型Polygon*,动态类型也是Polygon*
Shape *p1 = new Polygon(); // p1的静态类型是它声明的类型Shape*,动态类型p1是所指的对象Polygon*
Rectangle *pr = new Rectangle();
p1 = pr; // pB的动态类型可以改变,现在它的动态类型为Rectangle*
return 0;
}
如果一个类被继承,同时定义了基类以外的成员对象,而且基类析构函数不是virtual修饰的,那么当基类指针或者引用指向派生类对象并析构(例如自动对象在函数作用域结束时;或者通过delete)时,只会调用基类的析构函数而导致派生类定义的成员没有被析构,产生内存泄露等问题。虽然把析构函数定义成virtual的可以解决这个问题,但是当其他成员函数都不是virtual函数时,会在基类和派生类中引入vtable,实例引用入vptr造成运行时的性能损失。
如果确定不需要直接而是只通过派生类对象使用基类,可以把析构函数定义为protected(这样会使得基类和派生类外使用自动对象和delete时的错误,因为访问权限禁止调用析构函数),就不会导致以上问题。
从语法上来讲,一个函数被声明为protected或者private,那么这个函数就不能从“外部”直接被调用了。
对于protected的函数,子类的“内部”的其他函数可以调用之。
而对于private的函数,只能被本类“内部”的其他函数说调用。
通常使用的场景如下:
那你就可以将类A的构造函数/析构函数声明为protected,而将类A的子类的构造函数/析构函数声明为public。例如:
class A
{
protected:
A(){}
public:
….
};
class B : public A
{
public:
B(){}
….
};
A a; // error
B b; // ok
如果将构造函数/析构函数声明为private,那只能这个类的“内部”的函数才能构造这个类的对象了。这里所说的“内部”不知道你是否能明白,下面举个例子吧。
class A
{
private:
A(){ }
~A(){ }
public:
void Instance()//类A的内部的一个函数
{
A a;
}
};
上面的代码是能通过编译的。上面代码里的Instance函数就是类A的内部的一个函数。Instance函数体里就构建了一个A的对象。
但是,这个Instance函数还是不能够被外面调用的。为什么呢?
如果要调用Instance函数,必须有一个对象被构造出来。但是构造函数被声明为private的了。外部不能直接构造一个对象出来。
A aObj; // 编译通不过
aObj.Instance();
但是,如果Instance是一个static静态函数的话,就可以不需要通过一个对象,而可以直接被调用。如下:
class A
{
private:
A():data(10){ cout << “A” << endl; }
~A(){ cout << “~A” << endl; }
public:
static A& Instance()
{
static A a;
return a;
}
void Print()
{
cout << data << endl;
}
private:
int data;
};
A& ra = A::Instance();
ra.Print();
当我们想禁止在栈中产生对象时,如何来实现呢?
将构造函数设为private行吗?no!这样对象虽然不能在栈中产生,但同样也不能在堆中产生了。
将析构函数设为private行吗?bad!这样虽然限制了栈对象,但同时也限制了继承。
将析构函数设为protected行吗?yes!
例如:
class A
{
protected:
A() { }
~A() { }
public:
static A* create()
{ return new A() ;//调用保护的构造函数 }
void destroy()
{ delete this ;//调用保护的析构函数 }
};
我们可以这样来使用它:
A* pa= A::create();
pa->destroy() ;
有的时候,你想要一个类成为抽象类,但是刚好有没有任何纯虚函数。怎么办?因为抽象类是准备被用作基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类。:在想要成为抽象类的类里声明一个纯虚析构函数。
class awov {
public:
virtual ~awov() = 0; // 声明一个纯虚析构函数
};
这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:
awov::~awov() {} // 纯虚析构函数的定义
这个定义是必须的,因为虚析构函数工程的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
附上面试例题:
1、c++中如何阻止一个类被实例化?
2、一般什么时候构造函数被声明private?
3、什么时候编译器会生成默认的复制构造函数?
答:
1、当类中又被声明纯虚函数;将构造函数、析构函数声明private
2、当不想让用户在类外构造类的对象,阻止编译器产生默认复制构造函数。
3、用户未定义,且程序需要