为什么以及何时通过指针传递C ++中的类类型?

Why and when to pass class types in C++ by pointer?

考虑以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
class Abstract{
public:
    virtual void printStuff()=0;
};

class Derived: public Abstract{
public:
    void printStuff(){
        printf("Stuff
"
);
    }
};

现在,假设我想创建一个使用抽象类的printstuff方法的函数。在我了解到C++中只有一种方法是可能的之前,我认为有两种方式:指针越不明显,越明显,类似于你希望用int、字符等做的事情:

1
2
3
4
5
6
7
void ptr_function(Abstract* abs){ //non-obvious
    abs->printStuff();
}

void non_ptr_function(Abstract abs){ //obvious, analogous to, say, pow(a,b)
    abs.printStuff();
}

现在,我明白第二个在C++中是被禁止的。然而,我并不真正理解这种设计的根本原因。除了作为参数传递的指针对象和实际对象之外,上面的函数看起来不一样吗?

作为后续问题:构建必须将其他抽象类作为字段之一"包含"的类的首选方法是什么?如果这个问题的答案也是:"指针",那么我是否遗漏了一些东西,或者我是否必须自己跟踪这些对象的TimeLife(即手动删除它们)?对于非抽象类,这不是一个问题,就像我不使用指针一样,然后每当对象超出范围时,它就会自动删除(调用析构函数等)。但是如果我必须使用指针,看起来微管理需要很多不必要的时间和代码。

有没有更好的方法来解决这个问题?


不能按值传递抽象类,因为它无法实例化。在大多数情况下,传递值无论如何都是错误的(或者至少是非最佳的)。你要找的是通过引用:

1
2
3
4
void ref_function(Abstract & abs)
{
    abs.printStuff();
}

当您通过引用传递对ref_function内的abs所做的任何修改时,都将应用于存在于函数外部的同一实例。理想情况下,在您的测试用例中,您希望将对象作为const Abstract & abs传递,这将阻止对对象进行任何更改。在您的示例中,您需要将printStuff标记为const,以表示它不会更改其调用的对象-签名将更改为virtual void printStuff() const

为了回答你关于抽象类的所有权应该如何工作的另一个问题。记住,实际上不能有抽象类的实例,所以您所说的是通过抽象基类的句柄持有指向某个派生对象的指针。您可能希望为此使用std::unique_ptr,因为它将在类被销毁时正确删除所拥有的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Abstract
{
    public:
        virtual void Foo() = 0;
};

class Derived : public Abstract
{
    public:
        virtual void Foo() override {}
};

class MyClass
{
    public:
        MyClass();
    private:
        std::unique_ptr<Abstract> myObject;
};

MyClass::MyClass() : myObject(std::make_unique<Derived>())
{
}


实际上,将对象传递给函数有五种不同的可能性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Function declarations
void passByValue(Derived o);    //Not possible with an abstract class.
void passByReference(Abstract& o);
void passByConstReference(const Abstract& o);
void passByPointer(Abstract* o);
void passByConstPointer(const Abstract* o);

//Function calls
Derived o;
passByValue(o);
passByReference(o);      //May change o!
passByConstReference(o);
passByPointer(&o);       //May change o.
passByConstPointer(&o);

因为从passByReference(o)的调用中看不到调用可能修改变量o的内容,所以我从不使用pass-by-reference。我总是使用pass-by常量引用或pass-by指针。

然而,现在许多C++程序员通常憎恶指针的使用,因为它们不能像EDOCX1、7和EDCX1等8的智能指针玩得好。如果使用这些智能指针管理对象,则必须始终传递智能指针而不是裸指针/引用,通常通过常量引用:

1
2
void passBySmartPointer(const std::shared_ptr<Abstract>& o);
void passConstBySmartPointer(const std::shared_ptr<const Abstract>& o);

使用这种方法,您永远不会在代码中看到一个裸指针…

(需要传递智能指针的原因是不能破坏智能指针链:如果您将智能指针转换为裸指针,然后再转换回智能指针,则第二个智能指针不知道第一个,您将遇到麻烦。还有其他方法可以避免这种情况,但这远远超出了这个答案的范围。)