当前位置:  开发笔记 > 编程语言 > 正文

我什么时候可以使用前瞻性声明?

如何解决《我什么时候可以使用前瞻性声明?》经验,为你挑选了6个好方法。

我正在寻找允许在另一个类的头文件中进行类的前向声明的定义:

我是否允许为基类,作为成员持有的类,通过引用传递给成员函数的类等执行此操作?



1> Luc Touraill..:

让自己处于编译器的位置:当你转发声明一个类型时,所有编译器都知道这个类型存在; 它对它的大小,成员或方法一无所知.这就是为什么它被称为不完整类型.因此,您不能使用该类型来声明成员或基类,因为编译器需要知道该类型的布局.

假设以下前瞻性声明.

class X;

这是你能做什么,不能做的.

你可以用不完整的类型做什么:

将成员声明为指针或对不完整类型的引用:

class Foo {
    X *p;
    X &r;
};

声明接受/返回不完整类型的函数或方法:

void f1(X);
X    f2();

定义接受/返回指向不完整类型的指针/引用的函数或方法(但不使用其成员):

void f3(X*, X&) {}
X&   f4()       {}
X*   f5()       {}

你不能用不完整的类型做什么:

将它用作基类

class Foo : X {} // compiler error!

用它来声明一个成员:

class Foo {
    X m; // compiler error!
};

使用此类型定义函数或方法

void f1(X x) {} // compiler error!
X    f2()    {} // compiler error!

使用其方法或字段,实际上尝试取消引用具有不完整类型的变量

class Foo {
    X *m;            
    void method()            
    {
        m->someMethod();      // compiler error!
        int i = m->someField; // compiler error!
    }
};


对于模板,没有绝对规则:是否可以使用不完整类型作为模板参数取决于模板中使用类型的方式.

例如,std::vector要求其参数为完整类型,而boost::container::vector不是.有时,只有在使用某些成员函数时才需要完整类型; 例如,情况就是如此std::unique_ptr.

记录良好的模板应在其文档中指出其参数的所有要求,包括它们是否需要完整类型.


为"让自己置于编译器的位置"+1.我想象"编译器正在"留胡子.
这组规则忽略了一个非常重要的情况:您需要一个完整的类型来实例化标准库中的大多数模板.需要特别注意这一点,因为违反规则会导致未定义的行为,并且可能不会导致编译器错误.
很好的答案,但请参阅我的下面我不同意的工程点.简而言之,如果您没有包含您接受或返回的不完整类型的标题,则会强制对标题的使用者不可知的依赖性,而不必知道他们需要哪些其他人.
@JesusChrist:确切地说:当你按值传递一个对象时,编译器需要知道它的大小才能进行适当的堆栈操作; 当传递指针或引用时,编译器不需要对象的大小或布局,只需要地址的大小(即指针的大小),这不依赖于指向的类型.
@AndyDent:是的,但是标题的使用者只需要包含他实际使用的依赖项,所以这遵循C++原则"你只需支付你使用的东西".但实际上,对于期望标题是独立的用户来说可能是不方便的.

2> Timo Geusch..:

主要规则是,您只能转发声明其内存布局(以及成员函数和数据成员)不需要在您转发声明的文件中知道的类.

这将排除基类和除引用和指针使用的类之外​​的任何东西.


几乎.您还可以将"plain"(即非指针/引用)不完整类型称为函数原型中的参数或返回类型.

3> Marc Mutz - ..:

Lakos区分了课堂使用

    仅限名称(前向声明就足够了)和

    in-size(需要类定义).

我从来没有见过它更简洁明了:)


@Boon:我敢说...?如果你使用_only_类'_name_?
什么是名义上的意思?

4> j_random_hac..:

除了指向不完整类型的指针和引用之外,您还可以声明指定参数和/或返回不完整类型值的函数原型.但是,除非是指针或引用,否则无法定义具有不完整的参数或返回类型的函数.

例子:

struct X;              // Forward declaration of X

void f1(X* px) {}      // Legal: can always use a pointer
void f2(X&  x) {}      // Legal: can always use a reference
X f3(int);             // Legal: return value in function prototype
void f4(X);            // Legal: parameter in function prototype
void f5(X) {}          // ILLEGAL: *definitions* require complete types



5> R Sahu..:

到目前为止,没有一个答案描述何时可以使用类模板的前向声明.所以,在这里.

可以将类模板转发声明为:

template  struct X;

遵循接受的答案的结构,

这是你能做什么,不能做的.

你可以用不完整的类型做什么:

将成员声明为指针或对另一个类模板中不完整类型的引用:

template 
class Foo {
    X* ptr;
    X& ref;
};

声明一个成员是一个指针或对其不完整实例之一的引用:

class Foo {
    X* ptr;
    X& ref;
};

声明接受/返回不完整类型的函数模板或成员函数模板:

template 
   void      f1(X);
template 
   X    f2();

声明接受/返回其不完整实例化的函数或成员函数:

void      f1(X);
X    f2();

定义接受/返回指向不完整类型的指针/引用的函数模板或成员函数模板(但不使用其成员):

template 
   void      f3(X*, X&) {}
template 
   X&   f4(X& in) { return in; }
template 
   X*   f5(X* in) { return in; }

定义接受/返回其不完整实例之一(但不使用其成员)的指针/引用的函数或方法:

void      f3(X*, X&) {}
X&   f4(X& in) { return in; }
X*   f5(X* in) { return in; }

将它用作另一个模板类的基类

template 
class Foo : X {} // OK as long as X is defined before
                    // Foo is instantiated.

Foo a1; // Compiler error.

template  struct X {};
Foo a2; // OK since X is now defined.

用它来声明另一个类模板的成员:

template 
class Foo {
    X m; // OK as long as X is defined before
            // Foo is instantiated. 
};

Foo a1; // Compiler error.

template  struct X {};
Foo a2; // OK since X is now defined.

使用此类型定义函数模板或方法

template 
  void    f1(X x) {}    // OK if X is defined before calling f1
template 
  X    f2(){return X(); }  // OK if X is defined before calling f2

void test1()
{
   f1(X());  // Compiler error
   f2();     // Compiler error
}

template  struct X {};

void test2()
{
   f1(X());  // OK since X is defined now
   f2();     // OK since X is defined now
}

你不能用不完整的类型做什么:

使用其中一个实例作为基类

class Foo : X {} // compiler error!

使用其中一个实例来声明成员:

class Foo {
    X m; // compiler error!
};

使用其中一个实例来定义函数或方法

void      f1(X x) {}            // compiler error!
X    f2() {return X(); }   // compiler error!

使用其中一个实例化的方法或字段,实际上是尝试取消引用具有不完整类型的变量

class Foo {
    X* m;            
    void method()            
    {
        m->someMethod();      // compiler error!
        int i = m->someField; // compiler error!
    }
};

创建类模板的显式实例化

template struct X;


“到目前为止,没有答案描述何时可以向前声明类模板。” 这不仅仅是因为X和X <int>的语义完全相同,而且只有前向声明语法在任何实质性方面都不同,答案中除1行外的所有行仅等于Luc的和`s / X / X <int> / g`?真的需要吗?还是我错过了一个与众不同的微小细节?有可能,但是我在视觉上进行了几次比较,看不到任何东西。

6> yesraaj..:

在只使用指针或引用类的文件中.如果指针/引用,则不应调用任何成员/成员函数.

class Foo;//前向声明

我们可以声明Foo*或Foo&类型的数据成员.

我们可以使用Foo类型的参数和/或返回值声明(但不定义)函数.

我们可以声明Foo类型的静态数据成员.这是因为静态数据成员是在类定义之外定义的.

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有