Frage

getan Java für einige Jahre haben so nicht C ++ aufgespürt. Hat schließlich Klausel C ++ Ausnahmebehandlung in der Sprachdefinition hinzugefügt?

Gibt es ein bevorzugtes Idiom, das / finally Java Versuch nachahmt?

Am auch gestört, dass C ++ keine ultimative Supertyp für alle möglichen Ausnahmen haben, die ausgelöst werden könnte -. Wie Java Throwable Klasse

Ich kann schreiben:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

NACHTRAG EDIT:

  

ich am Ende die Antwort zu akzeptieren, dass   die meisten up Stimmen hatte, das heißt, die Verwendung   Destruktoren zu tun Bereinigung. Na sicher,   aus meinen eigenen Kommentaren, ist es klar, dass ich   nicht ganz damit einverstanden.   Jedoch C ++ ist, was es ist und so in   die Anwendung Bestreben Ich habe in   Geist, ich werde mehr oder weniger streben   um gemeinsame Gemeinschaft einzuhalten   trainieren. Ich werde Template-Klassen verwenden, um   wickeln Ressourcen, die nicht bereits haben   eine Klasse destructor (d, C-Bibliothek   Ressourcen), so dass auf sie schenk   destructor Semantik.

NEW NACHTRAG EDIT:

  

Hmm, anstelle von schließlich dann einem Verschluss   verfügen vielleicht? Verschluß in Kombination mit   Scopeguard Ansatz (siehe eine der   unten Antworten) wäre ein Weg sein,   erreichen Bereinigung mit beliebigen   Aktionen und Zugang zur Bereinigung   Code des äußeren Umfang Kontext. Cleanup konnte im Idiom Art und Weise erfolgen, die in Ruby-Programmierung zu sehen ist, wo sie Bereinigungsblocks liefern, wenn eine Ressource geöffnet wird. Ist das nicht ein   Verschluss-Funktion betrachtet für wobei   C ++?

War es hilfreich?

Lösung

Durch die effektive Nutzung von Destruktoren zu machen. Wenn eine Ausnahme in einem try-Block geworfen wird, wird sofort in sie jedes Objekt erstellt zerstört werden (und damit sein destructor genannt).

Dies unterscheidet sich von Java, wo Sie keine Ahnung haben, wenn ein Finalizer des Objekts aufgerufen werden.

UPDATE : Gerade aus dem Maul des Pferdes: Warum ++ nicht C bieten ein "endlich" konstruieren?

Andere Tipps

My $ .02. Ich habe wie C # und Java seit Jahren in verwalteten Sprachen programmieren, wurde aber den Schalter auf C ++ für die Zwecke der Geschwindigkeit zu machen gezwungen. Zuerst konnte ich nicht glauben, wie ich die Methode Signatur zweimal in der Header-Datei schreiben musste und dann der CPP-Datei, und ich wusste nicht, wie, wie es keine schließlich wurde blockiert, und keine Müllabfuhr bedeutete Tracking-Speicherlecks überall - Gosh ich mochte es gar nicht!

Aber, wie ich sagte, ich war gezwungen, C ++ zu verwenden. So war ich gezwungen, ernsthaft, es zu lernen, und jetzt habe ich endlich alle Programmierung Idiome verstanden wie RAII und ich alle Feinheiten der Sprache und so weiter. Es dauerte eine Weile, aber jetzt sehe ich, wie unterschiedlich von einer Sprache, die es zu C # oder Java verglichen wird.

In diesen Tagen denke ich C ++ ist die beste Sprache gibt es! Ja, ich kann verstehen, dass es ein wenig mehr ist, was ich ‚Spreu‘ (scheinbar unnötige Sachen zu schreiben) manchmal nennen, aber nach eigentlich die Sprache ernsthaft mit, ich habe meine Meinung über sie völlig verändert.

Ich habe Gedächtnis haben die ganze Zeit ausläuft. Früher habe ich alle meine Code in die .h-Datei zu schreiben, weil ich die Trennung von Code gehasst, konnte ich nicht verstehen, warum sie das tun würde! Und ich früher immer mit dummen zyklischen am Ende umfassen Abhängigkeiten und Haufen mehr. Ich war wirklich hing bis auf C # oder Java, mir C ++ unten ein großer Schritt war. In diesen Tagen ich es bekommen. Ich fast nie Speicherlecks hat, genieße ich die Trennung von Schnittstelle und Implementierung, und ich habe nicht mehr mit Zyklus Abhängigkeiten Probleme.

Und ich nicht verpassen die schließlich entweder blockieren. Um ehrlich zu sein, meine Meinung ist, dass diese C ++ Programmierer, die Sie über das Schreiben wiederholt Bereinigungsaktionen in catch-Blöcke nur mir klingen reden, wie sie gerade schlecht C ++ Programmierer sind. Ich meine, es nicht in diesen Thread wie keines der anderen C ++ Programmierer aussehen werden mit einer der Probleme, die Sie erwähnen. RAII wirklich macht schließlich überflüssig, und wenn überhaupt, dann ist es weniger Arbeit. Sie schreiben eine destructor und dann haben Sie nie ein anderes schließlich immer zu schreiben! Na ja, wenigstens für diesen Typ.

Bei allem Respekt, was ich denke, ist los ist Sie zu Java jetzt nur verwendet, so wie ich war.

C ++ 's Antwort ist RAII: Die destructor des Objekts ausgeführt wird, wenn sie sich außerhalb des Geltungsbereichs gehen. Ob durch eine Rückkehr, von einer Ausnahme oder was auch immer. Wenn Sie die Ausnahme irgendwo anders handhaben, können Sie alle Objekte aus der aufgerufenen Funktion bis zu Ihrem Handler sicher sein wird richtig, indem sie ihre destructor genannt zerstört werden. Sie werden für Sie aufzuräumen.

Lesen Sie http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

Nein hat schließlich nicht zu C hinzugefügt ++, noch ist es wahrscheinlich immer hinzugefügt werden.

Die Art und Weise C ++ Konstruktor / Destruktor verwendet macht die Notwendigkeit schließlich überflüssig.
Wenn Sie catch (...) zur Reinigung verwenden, dann verwenden Sie nicht C ++ richtig. Der Code Cleanup alle in dem destructor sein sollte.

Obwohl es nicht erforderlich ist, es zu benutzen C ++ eine std hat :: exception.
Erzwingen Entwickler aus einer bestimmten Klasse abzuleiten Ausnahme zu verwenden, geht gegen die Keep it simple Philosophie von C ++. Es ist auch, warum wir nicht alle Klassen erfordern von Object abgeleitet werden.

Lesen: Hat C ++ Unterstützung 'endlich' Blöcke? (Und was ist das ‚RAH‘ Ich höre immer wieder über?)

Die Verwendung von schließlich ist fehleranfälliger als Destruktoren aufzuräumen zu tun.
Dies liegt daran, dass Sie den Benutzer des Objekts zwingen eher zu tun, aufzuräumen als der Designer / Implementierer der Klasse.

Ok, ich habe in einer Antwort auf die Punkte hinzufügen, die Sie in einer separaten Antwort Post gemacht: (Es wäre viel bequemer, wenn Sie diese in die ursprüngliche Frage bearbeitet haben, so dass es nicht am unteren Ende unten die Antworten auf sie.

  

Wenn alle Bereinigungs immer getan wird in   Destruktoren dann würde es nicht brauchen   jeder Bereinigungscode in einer Raste zu sein   Block - noch C ++ hat Fangblöcke in denen   Bereinigungsaktionen zu erledigen. tatsächlich es   hat einen Block für catch (...), wo es   nur möglich Bereinigungsaktionen zu tun   (Na ja, kann sicherlich nicht auf jedem bekommen   Ausnahmeinformationen zu tun jeder   Logging).

Fang hat einen völlig anderen Zweck, und als Java-Programmierer sollten Sie sich bewusst sein, dass. Die finally-Klausel ist für „unbedingt“ Bereinigungsaktionen. Unabhängig davon, wie der Block verlassen wird, muss dies geschehen. Fang ist für die bedingte Bereinigung. Wenn diese Art von Ausnahme ausgelöst wird, müssen wir ein paar zusätzliche Aktionen auszuführen.

  

Die Bereinigung in einem schließlich blockieren   zu erledigen, ob es eine   Ausnahme ausgelöst oder nicht - das ist,   was man will immer passieren, wenn   Bereinigungscode vorhanden ist.

Wirklich? Wenn wir es wollen immer für diese Art geschehen (sagen wir, wir wollen immer eine Datenbankverbindung schließen, wenn wir damit fertig sind), dann warum nicht wir definieren es einmal ? In der Art selbst? Machen Sie die Datenbankverbindung schließt selbst, anstatt mit einer try / finally um es jede einzelne Anwendung setzen?

Das ist der Punkt in Destruktoren. Sie garantieren, dass jede Art der Lage ist, eigene Bereinigung zu kümmern, jedes Mal, es verwendet wird, ohne dass der Anrufer mit, daran zu denken.

  

C ++ Entwickler vom ersten Tag haben   geplagt mit, die Bereinigung zu wiederholen   Aktionen, die in catch-Blöcke erscheinen in   der Codefluss, der auftritt, bei der   erfolgreicher Ausstieg aus dem try-Block.   Java und C # Programmierer tun es einfach   einmal im finally-Block.

Nein. C ++ Programmierer haben nie damit geplagt. C-Programmierer haben. Und C-Programmierer, die erkannten, dass c ++ Klassen hatte, und rief dann selbst C ++ Programmierer haben.

I-Programm in C ++ und C # täglich, und ich glaube, ich von C # 's lächerlich Beharren geplagt bin, dass ich eine finally-Klausel (oder einen using Block) jedes Mal ich eine Datenbankverbindung oder etwas liefern muss sonst verwenden, die gereinigt werden müssen up.

C ++ lässt mich geben ein für alle Mal, dass „wenn wir mit dieser Art fertig sind, sollte es diese Aktionen durchführen“. Ich nicht Gefahr, Speicher freizugeben vergessen. Wage ich nicht zu vergessen, Datei-Handles, Steckdosen oder Datenbankverbindungen zu schließen. Weil mein Gedächtnis, meine Griffe, Steckdosen und DB-Verbindungen machen es selbst.

Wie kann es immer vorzuziehen sein, doppelten Bereinigungscode jedes Mal, wenn Sie einen Typen verwenden zu haben, zu schreiben? Wenn Sie die Art wickeln müssen, weil sie selbst keine destructor haben, haben Sie zwei einfache Möglichkeiten:

  • Geben Sie für eine richtige C ++ Bibliothek, die dieses destructor bietet (Hinweis: Boost)
  • Verwenden Sie boost :: shared_ptr es zu wickeln, und es mit einem benutzerdefinierten Funktors zur Laufzeit liefern, die Bereinigung Angabe gemacht werden.
  

Wenn Sie schreiben Anwendungsserver   Software wie Java EE Applikationsserver   Glassfish, JBoss usw., Sie sein wollen   Lage Ausnahme zu fangen und log   Informationen - im Gegensatz es zu lassen,   auf den Boden fallen. Oder noch schlimmer, fallen in   die Laufzeit und verursacht eine ungraceful   abrupter Ausgang des Anwendungsservers.   Deshalb ist es sehr wünschenswert ist, zu haben   eine übergeordnete Basisklasse für jeden   mögliche Ausnahme.   Und C ++ hat gerade eine solche Klasse. std :: exception.

     

C ++ getan seit den Tagen Cfront   und Java / C # die meisten dieses Jahrzehnts. ist   klar gibt es nur eine enorme zu sehen   Kultur Lücke, wie grundlegend   ähnliche Dinge angesprochen.

Nein, du hast noch nie C ++ getan. Sie haben mit Klassen Cfront oder C durchgeführt. Neint C ++. Es gibt einen großen Unterschied. Beenden Sie die Antworten lahm ruft, und man könnte etwas über die Sprache lernen Sie dachten, Sie wusste. ;)

Cleanup-Funktionen selbst sind gründlich lahm. Sie haben eine geringe Kohäsion, dass sie erwartet werden, eine Reihe von Aktivitäten in nur auszuführen Zusammenhang, wenn sie passieren. Sie haben eine hohe Kopplung, indem sie benötigen, um ihre Interna geändert haben, wenn die Funktionen, die tatsächlich etwas tun, geändert werden. Aus diesem Grund, sie sind fehleranfällig.

Der Versuch ... schließlich konstruieren ist ein Framework für die Bereinigung Funktionen. Es ist eine Sprache-ermutigt Weise lausigen Code zu schreiben. Darüber hinaus ermutigt, da sie den gleichen Bereinigungscode immer und immer wieder zu schreiben, untergräbt es das DRY-Prinzip.

Die C ++ Art und Weise ist für diese Zwecke bei weitem vorzuziehen. Der Bereinigungscode für eine Ressource wird genau einmal geschrieben, in dem destructor. Es ist an der gleichen Stelle wie der Rest des Codes für diese Ressource und damit guten Zusammenhalt hat. Der Bereinigungscode muss nicht in unabhängige Module gesetzt werden, und deshalb diese verkürzt sich auf Kupplung. Es wird genau einmal geschrieben, als gut gestaltet.

Darüber hinaus ist die C ++ Art und Weise ist viel gleichmäßiger. C ++, mit den Smart-Pointer-Hinzufügungen, kümmert sich um alle Arten von Ressourcen in der gleichen Art und Weise, während Java-Speicher gut behandelt und bietet unzureichende Konstrukte andere Ressourcen freizugeben.

Es gibt viele Probleme mit C ++, aber dies ist nicht einer von ihnen. Es gibt Wege, auf denen Java ist besser als C ++, aber dies ist nicht einer von ihnen.

Java wäre viel besser, mit einem Weg von RAII zu implementieren, anstatt versuchen ... schließlich.

Um zu vermeiden, eine Wrapper-Klasse für jede lösbare Ressource zu definieren, können Sie in Scopeguard interessiert sein ( http : //www.ddj.com/cpp/184403758 ), die ein "Reiniger" on the fly erstellen kann.

Zum Beispiel:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);

Ein Beispiel, wie schwierig es ist, endlich richtig zu verwenden.

Öffnen und Schließen zwei Dateien.
Wo wollen Sie garantieren, dass die Datei korrekt geschlossen ist.
für die GC Warten ist keine Option, da die Dateien wiederverwendet werden können.

In C ++

void foo()
{
    std::ifstream    data("plop");
    std::ofstream    output("plep");

    // DO STUFF
    // Files closed auto-magically
}

In einer Sprache ohne Destruktoren hat aber eine finally-Klausel.

void foo()
{
    File            data("plop");
    File            output("plep");

    try
    {
        // DO STUFF
    }
    finally
    {
        // Must guarantee that both files are closed.
        try {data.close();}  catch(Throwable e){/*Ignore*/}
        try {output.close();}catch(Throwable e){/*Ignore*/}
    }
}

Dies ist ein einfaches Beispiel und schon der Code gefaltet wird immer. Hier versuchen wir, nur zwei einfache Ressourcen Marschall. Aber da die Anzahl der Ressourcen, die erhöht werden müssen verwaltet und / oder deren Komplexität erhöht die Verwendung eines finally-Block wird immer schwieriger, richtig in Gegenwart von Ausnahmen zu verwenden.

Die Verwendung von schließlich bewegt sich die Verantwortung für die korrekte Verwendung auf den Benutzer eines Objekts. Durch die Verwendung von Konstruktor / Destruktor Mechanismus von C ++ zur Verfügung gestellt Sie die Verantwortung für die korrekte Verwendung für den Designer / Implementierer der Klasse bewegen. Dies ist inheritanly sicherer, da der Designer braucht es nur auf Klassenebene einmal korrekt zu tun (und nicht verschiedene Benutzer versuchen und tut es richtig auf unterschiedliche Weise).

C ++ 11 Verwenden mit seiner Lambda-Ausdrücke , ich habe seit kurzem mit dem folgenden Code finally zu imitieren:

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

Die FinallyGuard ist ein Objekt, das mit einem abrufbaren funktionsähnlichen Argumente, vorzugsweise einem Lambda-Ausdruck konstruiert ist. Es wird sich erinnern, einfach diese Funktion bis zu seiner destructor genannt wird, was der Fall ist, wenn das Objekt den Bereich verlässt, entweder aufgrund der normalen Kontrollfluss oder durch während der Ausnahmebehandlung Abwickeln stapeln. In beiden Fällen ruft der Destruktor die Funktion, wodurch den betreffenden Code ausgeführt wird.

Es ist ein bisschen seltsam, dass Sie den Code für die finally schreiben vor der Code für den try Block, aber abgesehen davon, dass es fühlt sich tatsächlich viel wie ein echter try / finally von Java . Ich denke, man sollte dies nicht für Situationen missbrauchen, wo ein Objekt mit seinem eigentümlichen destructor besser geeignet wäre, aber es gibt Fälle, in denen ich über mehr geeignet, diesen Ansatz in Betracht ziehen. Ich diskutierte ein solches Szenario in href="https://stackoverflow.com/q/15222502/1468366">.

Soweit ich die Dinge zu verstehen, wird std::function<void()> einige Zeiger indirection verwenden und mindestens einen virtuellen Funktionsaufruf seine Typ Löschung durchführt , so wird es ein Performance-Overhead sein. Verpassen Sie nicht diese Technik in einer engen Schleife verwenden, bei denen die Leistung entscheidend ist. In diesen Fällen ein spezielles Objekt, dessen destructor ist eine Sache wäre nur besser geeignet sein.

C ++ Destruktoren machen finally überflüssig. Sie können den gleichen Effekt erhalten, indem aus schließlich den Bereinigungscode bewegen zu Destruktoren entspricht.

Ich denke, dass Sie den Punkt fehlen, was catch (...) tun können.

Sie sagen in Ihrem Beispiel „ach, kann die Ausnahme nicht prüfen“. Nun, Sie haben keine Informationen über die Art der Ausnahme. Sie wissen nicht, auch wenn es so sogar einen polymorphen Typ ist, wenn Sie irgendeine Art von einem nicht typisierten Verweise auf sie haben, konnte man nicht einmal sicher ein dynamic_cast versuchen.

Wenn Sie wissen, über bestimmte Ausnahmen oder Ausnahme-Hierarchien, die Sie etwas mit dann tun können, ist dies der Ort für Catch-Blöcke mit explicity genannten Typen.

catch (...) ist nicht oft nützlich, in C ++. Es kann an Orten eingesetzt werden, die gewährleisten, dass sie nicht werfen, oder nur bestimmte vertraglich vereinbarte Ausnahmen werfen. Wenn Sie catch (...) für die Bereinigung verwenden, dann gibt es eine sehr gute Chance, dass Ihr Code nicht robust Ausnahme sicher in jedem Fall ist.

Wie in anderen Antworten erwähnt, wenn Sie lokale Objekte verwenden Ressourcen zu verwalten (RAH), dann kann es überraschend und aufschlussreich sein, wie wenige catch-Blöcke Sie benötigen, oft - wenn Sie müssen nichts tun, lokal mit einer Ausnahme -. auch der try-Block kann überflüssig sein, wie Sie die Ausnahmen lassen auf den Client-Code ausfließen, die auf sie reagieren können, während immer noch keine Ressourcenprobleme zu gewährleisten

Ihre ursprüngliche Frage zu beantworten, wenn Sie einige Stück Code müssen am Ende eines Blocks, Ausnahme oder ohne Ausnahme laufen, dann wäre ein Rezept sein.

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

Beachten Sie, wie wir mit try, catch und throw vollständig abschaffen.

Wenn Sie Daten in der Funktion haben, die ursprünglich außerhalb des Try-Blockes deklariert wurde, dass Sie Zugriff benötigt, um in dem „endlich“ Block, dann können Sie, dass an den Konstruktor der Hilfsklasse und speichern sie bis zum destructor hinzufügen müssen . An dieser Stelle mag ich jedoch ernsthaft überdenken, ob das Problem durch Änderung der Gestaltung der lokalen Ressourcen gelöst werden könnte Umgang mit Objekten, wie es etwas schief in der Gestaltung bedeuten würde.

Nicht immer vollkommen offtopic.

Kessel Plating DB Ressourcenbereinigung in Java

Sarkasmus-Modus: Ist nicht die Java wunderbar Idiom

?

Ich habe viel Klasse Design und Template-Wrapper-Design in C ++ über diesen 15 Jahren getan und es alle C ++ Art und Weise in Bezug auf die Destruktoren getan aufzuräumen. Jedes Projekt, auch wenn, beteiligte ausnahmslos die Verwendung von C-Bibliotheken, die Ressourcen zur Verfügung gestellt mit dem offenen es, verwenden Sie es, in der Nähe es Nutzungsmodell. Ein try / finally würde bedeuten, eine solche Ressource kann nur verzehrt werden, wenn es sein muss - in einer ganz robusten Art und Weise - und mit ihr geschehen. Der am wenigsten Langwierigkeit Ansatz, um diese Situation zu programmieren. Könnte mit all den anderen Zustand während der Logik dieser Bereinigung befassen geht ohne in irgendeiner Wrapper destructor scoped entfernt werden muss.

Ich habe die meisten meiner C ++ unter Windows Codierung so immer mit Microsofts __try / __ schließlich für solche Situationen zurückgreifen könnte. (Die strukturierte Ausnahmebehandlung hat einige leistungsstarke Fähigkeiten für mit Ausnahmen zu interagieren.) Ach, sieht nicht aus wie C-Sprache jemals eine tragbare Ausnahmebehandlung Konstrukte ratifiziert hat.

Das war nicht ideal, aber, weil es nicht einfach war, C und C ++ Code in einem Try-Block zu mischen, wo entweder Stil Ausnahme geworfen bekommen könnte. Ein finally-Block hinzugefügt, um C ++ wäre für Situationen hilfreich gewesen und würde Portabilität ermöglichen.

In Bezug auf Ihrem Nachtrags-edit, ja Verschlüsse werden für C ++ 0x betrachtet. Sie können mit RAII scoped Wachen verwendet werden, um eine einfach zu bieten Lösung zu verwenden, aktivieren Sie Pizer Weblog . Sie können auch Anprobe schließlich zu imitieren verwendet werden, finden Sie unter diese Antwort ; aber ist das wirklich eine gute Idee? .

Ich dachte, ich würde meine eigene Lösung dieses Add -. Eine Art Smart-Pointer-Wrapper für, wenn Sie mit nicht-RAH-Typen zu tun haben

wie folgt verwendet:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

Also hier ist die Umsetzung von Finaliser:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

... und hier ist Releaser:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

Ich habe ein paar verschiedene Arten von Releaser wie diese, darunter eines für freies () und einen für Closehandle ().

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top