C++ C++;模板函数中的动态内存分配

C++ C++;模板函数中的动态内存分配,c++,templates,C++,Templates,我有这个类和模板 class MyClass{ public: int data; }; template< typename Tp> Tp makeObj() { /// How is this allocated Tp obj; obj->data = 10; return obj; } class-MyClass{ 公众: int数据; }; 模板Tp makeObj() { ///这是如何分配的 Tp-obj; obj->da

我有这个类和模板

class MyClass{
public:
    int data;
};

template< typename Tp> Tp makeObj()
{
    /// How is this allocated
    Tp obj;
    obj->data = 10;
    return obj;
}
class-MyClass{
公众:
int数据;
};
模板Tp makeObj()
{
///这是如何分配的
Tp-obj;
obj->data=10;
返回obj;
}
和一个主要功能

int main()
{    
    /// Remove the next line and "makeObj" will return a null ptr
    /// I have no idea why    
    std::vector<int> vec;
    MyClass *tmp = makeObj<MyClass*>();
    std::cout << tmp->data << std::endl;
    std::cout << "\n\n | DONE3 -> " << std::endl;
    getchar();
    return 0;
}
intmain()
{    
///删除下一行,“makeObj”将返回空ptr
///我不知道为什么
std::vec;
MyClass*tmp=makeObj();

std::cout data让我们使用您拥有的模板函数

template< typename Tp> Tp makeObj()
{
    /// How is this allocated
    Tp obj;
    obj->data = 10;
    return obj;
}
现在您可以清楚地看到,实际上并没有分配
MyClass
对象,只分配一个指向该对象的指针。该指针将未初始化,取消对它的引用将导致未定义的行为

一种可能的解决方案是,模板参数不是指针,而是类型,然后在函数模板中显式使用指向它的指针:

// Declare the return-type to be a pointer
//                       v
template< typename Tp> Tp* makeObj()
{
    /// How is this allocated
    Tp* obj = new Tp;  // Declare variable as a pointer,
                       // and actually create an instance of the type
    obj->data = 10;
    return obj;
}
//将返回类型声明为指针
//五
模板Tp*makeObj()
{
///这是如何分配的
Tp*obj=new Tp;//将变量声明为指针,
//并实际创建该类型的实例
obj->data=10;
返回obj;
}
把它当作

// Explicitly declares as a pointer
//      v
MyClass *tmp = makeObj<MyClass>();
//                     ^^^^^^^
// And pass the plain class-type as the template argument, not a pointer
//显式声明为指针
//五
MyClass*tmp=makeObj();
//                     ^^^^^^^
//并将普通类类型作为模板参数而不是指针传递

您的函数没有返回有效的指针,而是返回堆栈上发生的任何内容。包含“std::vector vec;”行后,make-_obj中堆栈的内容会发生更改


你所拥有的是纯粹的未定义行为。

其实很简单:你使用它的方式,
Tp
被视为一个值。它可能是一个指针并不重要,因为指针是值。你可以将指针想象为
intptr\t
,并添加了一些额外的东西。因此,
makeObj
创建了指针的一个实例。但是这个inter没有指向任何东西。因此,
obj->data=10
是未定义的行为。从这一点开始,您的代码可以做任何事情。这就是分析的结束。您需要修复
makeObj
。我想
makeObj
应该是一个工厂函数

下面是一个例子,说明代码< > MaulObj> <代码>。你真的不想被原始指针弄糊涂。工厂函数应该返回<代码> STD::UnQuyJPPT<代码>或等价(例如,代码> KJ::Outs< /Cux>)。这就是习惯的现代C++应该如何看。

您也不需要使用控制台i/o来确认事情是否按预期工作:您可以
assert
,调试器将在断言失败时停止。从这一点到测试驱动开发还有一小步;您可以使用“assert”的变体这是特定于您所使用的测试框架的,但是即使没有测试框架,当您处理东西时,使用断言也很容易检查

<> >鉴于我们生活在浏览器中可以访问C++编译器和调试器的奇妙时代,您可以在下面的代码中对

进行实验
#包括
#包括
模板std::unique_ptr makeObj(Args&&…Args){
自动ptr=std::使_唯一(std::forward(args)…);
ptr->data=10;
返回ptr;
}
结构MyType{
int foo={};
int data={};
MyType()=默认值;
MyType(intfoo,intdata):foo(foo),data(data){
};
int main()
{
自动t=makeObj(5,20);
断言(t->foo==5);
断言(t->data==10);
自动u=makeObj();
断言(u->foo==0);
断言(u->data==10);
}

是的,现在我按照您的建议使用模板,我无意中使用了前面的代码(有问题的代码),但是为什么我仍然得到一个有效的指针,输出“10”?@asit欢迎来到的奇妙世界。仅仅因为代码可以编译并且似乎可以工作并不意味着它是正确的。一个像样的编译器(可以使用标志或选项启用更多警告)将告诉您在
makeObj
中使用未初始化变量的情况。编译器返回以下警告警告:变量“obj”在此处使用时未初始化注意:在函数模板专门化的实例化中,此处请求“makeObj”注意:初始化变量“obj”以使此警告静音此代码被隐藏在一个较大的呃代码,它编译好了,工作正常了,我就是走向了灾难。
// Explicitly declares as a pointer
//      v
MyClass *tmp = makeObj<MyClass>();
//                     ^^^^^^^
// And pass the plain class-type as the template argument, not a pointer
#include <cassert>
#include <memory>

template <class T, class... Args> std::unique_ptr<T> makeObj(Args &&... args) {
  auto ptr = std::make_unique<T>(std::forward<Args>(args)...);
  ptr->data = 10;
  return ptr;
}

struct MyType { 
    int foo = {};
    int data = {};
    MyType() = default;
    MyType(int foo, int data) : foo(foo), data(data) {}
};

int main()
{
    auto t = makeObj<MyType>(5, 20);
    assert(t->foo == 5);
    assert(t->data == 10);
    auto u = makeObj<MyType>();
    assert(u->foo == 0);
    assert(u->data == 10);
}