C++ std::static_pointer_cast详解

一、概述

static_pointer_cast从表面上看就是静态指针类型转换。细细看来,并不是那么简单,有一个隐形的限制条件。首先这个是c++11里的,更老的编译器不支持,其次指针是shared_ptr类型的,对于普通指针是无效的。还有一般只用在子类父类的继承关系中,当子类中要获取父类中的一些属性时,或工厂模式等需要通过父类参数接收不同子类实例的场景(当然了子类通过多态拥有自己的父类继承来的属性和行为,但是还想知道父类相应的属性和行为,这时,将父类的shared_ptr通过static_pointer_cast转化为子类的shared_ptr,这样就可以使得子类可以访问到父类的方法)。

这个函数返回的是shared_ptr类型sp的一份共享拷贝,只不过它指向的对象类型从原来的U变为T。

定义:

template <class T, class U>
  shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp) noexcept;

解释:

Returns a copy of sp of the proper type with its stored pointer casted statically from U* to T*.

If sp is not empty, the returned object shares ownership over sp‘s resources, increasing by one the use count.

If sp is empty, the returned object is an empty shared_ptr.

二、实测

1.例1

// static_pointer_cast example
#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
  B() { dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";

int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;

  foo = std::make_shared<A>();

  // cast of potentially incomplete object, but ok as a static cast:
  bar = std::static_pointer_cast<B>(foo);

  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}

结果:

foo's static  type: class A
foo's dynamic type: class A
bar's static  type: class B
bar's dynamic type: class A

不用std::static_pointer_cast

// static_pointer_cast example
#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
  B() { dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";

int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;

  foo = std::make_shared<A>();

  // cast of potentially incomplete object, but ok as a static cast:
  bar = std::make_shared<B>();
  //bar = std::static_pointer_cast<B>(foo);

  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}

结果

foo's static  type: class A
foo's dynamic type: class A
bar's static  type: class B
bar's dynamic type: class B

事实上,当在调用下面这句话时,B对象没有被构造(分配内存),bar是指向foo对象的。

// static_pointer_cast example
#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { std::cout << "construct A " << std::endl; dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
 B() { std::cout << "construct B " << std::endl; dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";
int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;
  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  foo = std::make_shared<A>();
  // cast of potentially incomplete object, but ok as a static cast:
  bar = std::static_pointer_cast<B>(foo);
  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}

输出:

foo's static  type: class A
bar's static  type: class B
construct A 
foo's static  type: class A
foo's dynamic type: class A
bar's static  type: class B
bar's dynamic type: class A

2.例2

再看以下转变过程

// static_pointer_cast example
#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { std::cout << "construct A " << std::endl; dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
 B() { std::cout << "construct B " << std::endl; dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";
int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;
  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  foo = std::make_shared<A>();
  bar = std::make_shared<B>();
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';
  // cast of potentially incomplete object, but ok as a static cast:
  bar = std::static_pointer_cast<B>(foo);

  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}

输出

foo's static  type: class A
bar's static  type: class B
construct A #构造A
construct A #B继承A,构造B时先构造A
construct B 
bar's static  type: class B
bar's dynamic type: class B #B构造后它的dynamic type为class B
foo's static  type: class A
foo's dynamic type: class A
bar's static  type: class B
bar's dynamic type: class A #使用static_pointer_cast转换后dymamic type变为class A

三、结论

通过各种测试,基本可以看出来,static_pointer_cast可以将父类参数强制转换为子类类型,并继承子类的static属性。实际应用中,可以将方法参数定义为父类型,但实际传入子类型,通过static_pointer_cast获得子类特有的方法和static属性,类似下方的应用场景。

struct ContextTest : public ContextBase {
    ~ContextTest() {
        PINFO << "[TaskFlow] Done! Context content is: " << content;
    }
    std::string content;
};
auto taskflow_ = std::make_shared<TaskFlow>(true);
auto taskA = taskflow->CreateSubTask([](std::shared_ptr<ContextBase>& c){
        static int a_cnt = 0;
        auto ctx = std::static_pointer_cast<ContextTest>(c);    // 将ContextBase参数c强制转换为子类ContextTest类型,并继承ContextTest的static属性
        ctx->content += "task A done in round " << std::to_string(a_cnt) << "; " ;
 });

auto ctx = std::make_shared<ContextTest>(); 
ctx->content = "Round " + std::to_string(i) + " : "; 
taskflow->Start(ctx);

 

22.6.28

欢迎关注下方“非著名资深码农“公众号进行交流~

发表评论

邮箱地址不会被公开。