-1

当我惊讶于下面的代码没有按预期工作时,我正在玩模板:

#include <iostream>
#include <string>
#include <cstring>

template <class Object>
class CreatorTWO {
public:
  CreatorTWO (void) {}
  ~CreatorTWO (void) throw () {}
  template <typename... Object_Params>
  Object* create (const Object_Params&... params_) {
    Object _temp_obj = Object(params_...);
    size_t _obj_bytes = sizeof (_temp_obj);
    void * _mem_block = std::malloc (_obj_bytes);
    std::memmove(_mem_block,&_temp_obj,_obj_bytes);
    //The line below prints the dereferenced object before it is returned:
    std::cout << *(static_cast <Object*> (_mem_block)) << '\n';
    return static_cast <Object*> (_mem_block);
  }
};

int main (int argc, char* argv[]) {
  CreatorTWO <std::string> _c2;
  std::string* _strp = _c2.create("Hello");
  std::cout << *_strp << '\n';
  std::free (_strp);
  return 0;
}

上面的代码应该创建一个传递给它的参数数量不同的对象。但是,当我创建一个以 std::string 为模板的实例时,传递“Hello”参数应该会给我一个指向包含“Hello”的字符串的指针。然而,这种情况并非如此。如果你运行上面的代码,pre 和 post 的值是不同的,pre 是正确的。有谁知道是什么导致了这种不良行为?谢谢。

4

1 回答 1

1

C++ 在对底层硬件的原始访问和能够快速开发的高级语言之间处于一个不舒服的位置。

一般原则是您不能使用 memcpy 移动对象,在这种情况下您已经破坏了该对象。

当你创建一个类时。

 class Example {
     protected:
        char * ptr;
        size_t len;
     public:
        Example( const char * str ) {
            len = strlen( str );
            ptr = new char[ len  + 1];
        }
        virtual ~Example() {
             delete [] ptr;
        }
        Example & operator=( const Example & rhs ) {
            if( &rhs != this ) {
                delete [] ptr;
                len = rhs.len();
                ptr = new char[ len + 1 ];
                strcpy( ptr, rhs.ptr );
            }
        }
        Example( const Example & src ) {
            len = src.len;
            ptr = new char[ len + 1];
            strcpy( ptr, src.ptr );
        }
        Example() {
            ptr = new char[1];
            ptr[0] = '\0';
            len = 0;
        }
 };

访问内部状态的想法阻止了类的设计者能够保证正确性。

如果类 Example 已被 memcpy'ed,那么将有该类的两个实例,并且都设置了值 ptr。如果其中一个被破坏,则它释放另一个由 ptr 寻址的内存,从而破坏其他类的内部状态。

当一个类已经很好地实现时,使用 operator= 进行复制可以让设计者了解什么可以共享,什么需要复制,并且可以确保正确的行为。

由于这个原因,通过使用原始内存运算符修改或复制一个类会导致未定义的行为。

于 2017-09-02T10:53:10.730 回答