LOADING

加载过慢请开启缓存,浏览器默认开启

C++ -interview ptr&cast

2023/3/17 interview C++

Smart pointer

1.shared pointer

读线程安全,写需要加锁
shared pointer允许多个指针指向某个内存区域

以下为简单实现

class Counter{
    private:
        uint64_t cnt=0;
    public:
        Counter():cnt(0){}
        Counter(const Counter& c)=delete;
        Counter& operator=(const Counter& c)=delete;
        ~Counter(){}
        void reset(){
            this->cnt=0;
        }
        uint64_t get(){
            return this->cnt;
        }
        void operator++(){
            this->cnt++;
        }
        void operator++(int){
            this->cnt++;
        }
        void operator--(){
            this->cnt--;
        }
        void operator--(int){
            this->cnt--;
        }
        friend ofstream& operator<<(ofstream& out,const Counter& c){
            out<<c.cnt<<'\n';
            return out;
        }
};
template<typename T>
class Shared_ptr{
    private:
        Counter* counter;
        T* ptr;
    public:
        explicit Shared_ptr(T* ptr=nullptr){
            this->counter=new Counter();
            this->ptr=ptr;
            (*this->counter)++;
        }
        Shared_ptr(Shared_ptr<T>& sp){
            this->counter=sp.counter;
            this->ptr=sp.ptr;
            (*this->counter)++;
        }
        T* get(){
            return this->ptr;
        }
        uint64_t get_cnt(){
            return this->counter->get();
        }
        T& operator*(){
            return *this->ptr;
        }
        T* operator->(){
            return this->ptr;
        }
        ~Shared_ptr(){
            (*counter)--;
            if(this->counter->get()==0){
                delete counter;
                delete this->ptr;
            }
        }
        friend ostream& operator<<(ostream& out,Shared_ptr<T>&ptr){//watch out for the &,if not then call Shared_ptr(Shared_ptr<T>& sp)
            out<<"address is"<<ptr.get()<<'\n';
            out<<"counter is"<<ptr.get_cnt()<<'\n';
            return out;
        }
};

2.unique_ptr

没有构造函数,所以不支持普通的拷贝和赋值操作
不允许copy和赋值操作

unique具有唯一性,对指向的对象值存在唯一的unique_ptr。unique_ptr不可复制,赋值,但是move()可以转换对象的所有权,局部变量的返回值除外。与shared_ptr相比,若自定义删除器,需要在声明处指定删除器类型,而shared不需要,shared自定义删除器只需要指定删除器对象即可,在赋值时,可以随意赋值,删除器对象也会被赋值给新的对象。

3. weak_ptr

1.weak_ptr只能从shared_ptr对象构建。

2.weak_ptr并不影响动态对象的生命周期,即其存在与否并不影响对象的引用计数器。当weak_ptr所指向的对象因为shared_ptr计数器为0而被释放后,那么weak_ptr的lock方法将返回空。

3.weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象。

4.提供了expired()与lock()成员函数,前者用于判断weak_ptr指向的对象是否已被销毁,后者返回其所指对象的shared_ptr智能指针(对象销毁时返回”空“shared_ptr),如果返回shared_ptr,那么计数器会加1.

解决了如下问题
1 当你想使用对象,但是并不管理对象,并且在需要时可以返回对象的shared_ptr时,则使用

2.解决shared_ptr的循环引用问题

class A;
class B{
   public:
   ~B() {
     std::cout << "B destory, a_ptr use_count:" << a_ptr.use_count() << "\n";
   }

   std::shared_ptr<A> a_ptr;
 };

class A{
public:
    ~A() {
        std::cout << "A destory, b_ptr use_count:" << b_ptr.use_count() << "\n";
    }

    std::shared_ptr<B> b_ptr;
};
int main(){
  shared_ptr<int>p1(new int(3));
  shared_ptr<int>p2(p1);
  weak_ptr<int>pp1(p1);
  cout<<pp1.use_count()<<'\n';
  p2.reset();
  cout<<pp1.use_count()<<'\n';
  cout<<*pp1.lock()<<'\n';
  p1.reset();
  cout<<pp1.use_count()<<'\n';
  cout<<pp1.expired()<<'\n';

  std::shared_ptr<A> a(new A());
  std::shared_ptr<B> b(new B());
  a->b_ptr=b;
  b->a_ptr=a;

  std::cout << "A:" << a.use_count() << "\n";
  std::cout << "B:" << b.use_count() << "\n";
  return 0;
}

4.auto ptr

被废弃,主要支持以下功能
1.支持拷贝构造
2.支持赋值拷贝
3.支持operator->/operator*解引用
4.支持指针变量重置
5.保证指针持有者唯一

会有很多所有权转移的问题,比如如下的代码

std::auto_ptr<People> one = peoples[5];

这会把所有权进行交换,这是因为auto ptr实现了=,然后所有权就交换了

auto_ptr 可以赋值拷贝,复制拷贝后所有权转移;unqiue_ptr 无拷贝赋值语义,但实现了move 语义;
auto_ptr 对象不能管理数组(析构调用 delete),unique_ptr 可以管理数组(析构调用 delete[] );

Cast

static_cast

1.本质上是C强制转换的替代品,因为C的强制转换不好找,所以采用static_cast

2.对于C风格的强制转换来说,假如你把一个char强制转换成int\,这在C里面是可以实现的,但是运行的时候是会出错的,但是如果你使用static_cast,那么预编译的时候就会出错,让你没法编译惹

3.对于私有继承基类的子类来说,是不允许转换为基类指针的,但是用C强制转换可以,所以我们需要用static_cast来在预编译的时候发现错误

4.有时候,假如两个子类都继承同一个基类,你分别把该两个子类转换为基类,然后把拿到的转换为另一个子类,这样做在static_cast的时候可以过编译,所以我们在这种情况下不该使用它

5.处理void*的时候,记得用static_cast!

class Int{
    private:
        int x;
    public:
        Int(int a):x(a){
            cout<<"call contructor!\n";
        }
        operator string(){
            cout<<"call operator!\n";
            return to_string(this->x);
        }
        int value(){
            return this->x;
        }
        friend ostream& operator<<(ostream& out,Int& a){
            out<<a.x<<'\n';
            return out;
        }
};
class Dad{

};
class Son:private Dad{

};
class Son_1:public Dad{

};
class Son_2:public Dad{

};
int main(){

  float a_f=1.1;
  int a_i=static_cast<int>(a_f);
  assert(a_i==1);

  Int obj(3);
  cout<<obj<<'\n';
  string obj_str=static_cast<string>(obj);
  assert(obj_str=="3");

  obj=static_cast<Int>(9);
  assert(obj.value()==9);

  int* b_int=new int;
  char* b_char=new char;
  //b_int=(int*)(b_char);//could pass,but wrong in runing

  // b_int=static_cast<int*>(b_char);//fail since static_cast would check!

  Son s;
  //Dad* d=(Dad*)&s;//could pass wrong in runing since it is private inherient

  //Dad* d=static_cast<Dad*>(&s);//could not pass unless it is public

  Son_1 s1;
  Son_2 s2;
  Dad* d1=static_cast<Dad*>(&s1);
  Dad* d2=static_cast<Dad*>(&s2);

  Son_2* s22=static_cast<Son_2*>(d1);//should never use static_cast!
  Son_1* s11=static_cast<Son_1*>(d2);

  int i=10;
  void* ptr=static_cast<void*>(&i);//always use static_cast
  int* new_i=static_cast<int*>(ptr);
  return 0;
}

2. dynamic cast

1.dynamic_cast是用于处理static_cast的上一个问题的

2.用于在正确的时候对基类向子类进行cast

3.需要多态的特性才能进行使用(至少有一个virtual function)

4.runtime check 所以性能你懂的

5.如果转换的新类型是引用类型,throw一个错误

class Dad{
    virtual void print(){
        cout<<"I am a dad\n";
    }
};
class Son:public Dad{
    void print()override{
        cout<<"I am a son\n";
    }
};
class Daughter:public Dad{
    void print()override{
        cout<<"I am a Daughter!\n";
    }
};
int main(){
  Son son1;
  Dad* d=dynamic_cast<Dad*>(&son1);
  assert(d!=nullptr);
  Daughter* dau=dynamic_cast<Daughter*>(d);
  assert(dau==nullptr);
  try{
    Daughter& dd=dynamic_cast<Daughter&>(son1);
  }catch(std::exception& e){
    std::cout<<e.what()<<"wrong!"<<'\n';
  }
  return 0;
}

3. reinterpret_cast

1.用于对某个结构体的重interpret

2.需要很小心的使用

3.用于位的重定义

class Man{
    public:
    void say_man(){
        cout<<"I am a man\n";
    }
};
class Woman{
    public:
    void say_woman(){
        cout<<"I am a woman\n";
    }
};
struct node{
    int x,y;
    char c;
    bool b;
};
int main(){
  Man* m=new Man;
  Woman* w=new Woman;

  m->say_man();
  Man* new_man=reinterpret_cast<Man*>(w);//should not use it!
  new_man->say_man();

  //when you should use re_interpret_cast
  node s;
  s.y=1;s.c='a';s.x=2;s.b=false;
  int* p=reinterpret_cast<int*>(&s);
  cout<<*p<<'\n';
  ++p;
  cout<<*p<<'\n';
  ++p;
  cout<<*(reinterpret_cast<char*>(p))<<'\n';
  return 0;
}

4.const_cast

用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 )

需要原数据不是constant

另一个用法是当你需要往某个需要变量的函数里传constant的时候,需要用const_cast来取消constant

void transfer(int* x){
    cout<<"1\n";
}
int main(){
  const int a=1;
  const int* b=&a;
  int* c=const_cast<int*>(b);// now removed the const
  *c=2;//invalid and undefine behavior, should not use!
  cout<<a<<'\n';//would not change,since compiler would directly write the constant value for print

  int aa=1;
  const int* bb=&aa;
  int* cc=const_cast<int*>(bb);
  *cc=3;
  cout<<aa<<'\n';

  const int v=1;
  const int* pv=&v;

  transfer(const_cast<int*>(pv));//ok
  //transfer(pv);//panic!
  return 0;
}

shared_ptr的问题

#include <bits/stdc++.h>

using namespace std;

class A{
public:
    A(){};
    A(int a): age(a) {};
    ~A(){};
private:
    int age;
};

int main()    {
    A *a = new A(10);
    shared_ptr<A> p1(a);
    shared_ptr<A> p2(a);
}

当p1和p2超出作用域被销毁时,仍旧不会销毁,这个时候use_cnt都是1,我们需要这样修复

shared_ptr<A> p1(new A(10));
shared_ptr<A> p2(p1);

可以直接构造一个weak_ptr吗?

可以构造一个 weak_ptr 对象,但是需要注意它所引用的对象必须已经被一个 shared_ptr 管理。这是因为 weak_ptr 并不拥有资源,它只是对一个 shared_ptr 所管理的对象进行弱引用,可以用于观察这个对象的状态,但是并不会增加资源的引用计数。

std::move

1.强制转换左值为右值引用

std::string str = "Hello";
std::string&& rref = std::move(str);

2.使用移动语义进行赋值操作

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2;
v2 = std::move(v1);

3.使用移动语义进行参数传递

void func(std::vector<int>&& v);
std::vector<int> v = {1, 2, 3};
func(std::move(v));