go to start Questions For Exam Preparation HS17
|home |print view |recent changes |changed January 22, 2018 |
exact
|You are 54.198.3.15 <- set your identity!

Sections: Questions via email by MikeMarti: | Questions via email by RolfFurrer: | Antworten von ThomasCorbat: |

Place your questions here up to Mon Jan 8, 2018. If there are no questions posted, we (Thomas, Peter) will come unprepared to the preparation week on Wed Jan 10, 2018.


Questions via email by MikeMarti: ^

  1. std::next_permutation: Wie speichert es, welche Permutationen bereits aufgerufen wurden? => tut es nicht => wie funktionierts dann?
  2. Warum muss die Base-Class einen virtual Destructor haben, wenn man einen unique pointer von dieser verwendet und warum brauchts beim shared pointer keinen?
  3. Wie kann man den Effekt von final erhalten, ohne das Keyword zu verwenden? (sind ja angeblich beide superfluous)
  4. Muss eine Derived-Klasse eine pure virtual function immer Überschreiben? (wie abstract in Java / C#) Wenn nicht, was passiert bei einem Aufruf?
  5. Was bedeutet explicit bei einem Konstruktor? Was könnte passieren, wenn mans vergisst? (kurzes Beispiel)
  6. stream.clear(std::ios::failbit) geht das oder ist das ein Fehler? (Video 5 am Ende) Sollte doch stream.setstate() sein.
  7. Folie 6 S.15: Warum wäre ein g(two::type_two) im Namespace two ambiguous? Durch ADL sollte doch diese neue Implementation beim letzten Aufruf ausgewählt werden.
  8. Wird zuerst der globale Namespace oder ADL betrachtet, wenn der Globale der Eigene ist?
  9. for (std::string s : { "Fall", "leaves", "after", "leaves", "fall" }) {...} Von welchem Typ ist der rechte Teil? Array? Initializer List?
  10. Ist ein multiset ein sortierter Vektor (vom Verhalten)? Wenn ja, warum braucht man ihn, wenn nein, was ist der grosse Unterschied?
  11. Kann ein Stream beim lesen eines Chars in den Failzustand gehen (encoding)?
  12. Warum unterscheidet man zwischen std::cend und std::end, wenn man std::cend sowohl für const als auch für non const Iteratornen verwenden kann?
  13. Werden operator-Funktionen vom Compiler in richtige Funktionsaufrufe umgewandelt?
  14. Mit was ist ein Vektor gefällt, wenn er als Typ eine Klasse ohne leeren Konstruktor hat und man eine Defaultgrösse angibt, also z.B. std::vector<MyClassNoEmptyConstructor> v (10)?
  15. Wenn man einem std Algorithmus einen Functor mit Templateparameter übergibt, muss man die Typen nicht angeben. Ist dies so, da der Übergabewert vom Algorithmus ebenfalls ein Template-Parameter ist (bei Klassen funktioniert die Template Argument Deduction ja nicht)? Warum muss man sie bei einem ostream_iterator bei std::copy angeben?
  16. std::begin(this) oder std::begin(*this), wenn man von einem Container erbt?

Antworten ThomasCorbat:

  1. Für die Bestimmung der Permutation ist die lexikographische Sortierung relevant. Dabei ist die erste Permutation (Ausgangslange) ein aufsteigend sortierter Range. Entsprechend ist der absteigend sortierte Range das "Ende". Der Zustand ist also der übergebene Range.
  2. Der shared_ptr merkt sich die zu verwendende Operation (Deleter) für die konkrete Instanz. Dies bedeutet einen geringen Overhead. Der unique_ptr hingegen benutzt den delete operator (Thema von C++ Advanced). Dadurch wäre es Undefined Behavior ein Objekt einer abgeleiteten Klasse über einen unique_ptr der Basis-Klasse zu löschen wenn der Destruktor der Basis-Klasse nicht virtual ist.
  3. Gar nicht. Aber es gibt in der Realität selten Fälle in welchen es sinnvoll ist ein weiteres Overriding zu verbieten. Der Effekt ist nur für den Compiler relevant, wodurch gewisse Programme ungültig werden, und hat auf das ausführbare Programm keine Auswirkung.
  4. Eine Klasse, die eine Pure Virtual Member Funktion hat ist abstrakt. Eine Klasse, welche von einer abstrakten Klasse abgeleitet ist, ist ebenfalls abstrakt, ausser sie implementiert die Pure Virtual Member Function. Eine abstrakte Klasse kann nicht instanziiert werden. Damit kannst du kein Programm schreiben, in welchem du ein Objekt hast, für welches eine Member Function pure virtual ist. Man kann nun aber die Funktion trotzdem aufrufen, zum Beispiel in der abgeleiteten Klasse mit Base::foo(). Dies ist dann aber als würde man eine deklarierte Funktion, welche einfach nicht implementiert ist aufrufen: Der Linker beschwert sich, dass er diese Funktion nicht findet. Was nun sehr speziell ist: Falls man diese Funktion trotzdem implementiert hat, wird sie auch gefunden. Also könnte man theoretisch Pure Virtual Member Functions implementieren. (Beispiel 4)
  5. Eine implizite Konvertierung könnte stattfinden (sofern es sich um einen Konstruktor für ein einzelnes Argument handelt). Beispiel 5, ohne explicit. Dies kann zu überraschenden Aufrufen oder ungewollten Konvertierungen führen.
  6. Das geht. Hier hat man schlechte Namen gewählt. setstate() setzt zusätzlich das übergeben Flag. clear() löscht alle Flags und setzt das übergebene.
  7. Durch ADL kommt lediglich ein weiterer Namespace für den Lookup hinzu. Damit wären ::g(::two::type_two) und ::two::g(::two::type_two) eine Option.
  8. Gleiches wie bei Frage 7. Der Globale Namespace umfasst alle Namespaces. Es gibt keine Priorität dabei. Wichtig ist dabei, dass der Compiler nur Deklarationen sieht die vor der Aufrufstelle vorkommen erkennt.
  9. std::initializer_list<char const *>
  10. Nein ein multiset ist kein sortierter vector. Ein vector könnte nicht dieselben Garantien für die Laufzeit der Member Operationen geben wie das multiset. Zudem haben beide ein leicht unterschiedliches Interface (vector hat zum Beispiel push_back() und multiset equal_range()). Abgängig von der Verwendung kann das eine oder andere effizienter sein. Sofern der vector oft neu alloziert werden muss oder viele Elemente darin verschoben werden, ist das multiset voraussichtlich effizienter. Da der vector aber ausserordentlich gut von Cache-Hits profitieren kann, muss man das kokrete Problem mit realen Daten profilen, um eine absolute Aussage zu erhalten.
  11. Ja, falls der Stream keine Daten mehr enthält, ist er nach dem Einlesen eines chars auch im Fail-State (egal ob mit get() oder >> eingelesen)
  12. cend() gibt es erst seit C++11. Man könnte auch nur end() verwenden, welches automatisch einen passenden iterator (const_iterator oder iterator) zurückliefert, abhängig davon ob der container const oder non-const ist. Es kommuniziert klarer die Absicht bei der Iteration, wenn man explizit cend() verwendet und verhindert garantiert eine unbeabsichtigte Modifikation der Elemente.
  13. Ja, sofern er sich um Operationen auf Objekten handelt. Und wenn möglich bei entsprechender Optimierungseinstellung wieder entfernt.
  14. Was verstehst du unter einem «leeren» Konstruktor? Den Default-Konstruktor? Wenn dieser nicht existiert kann man auch kein vector dieses Typs per size-Konstruktor erstellen. Das verhindert der Compiler. Falls du einfach einen leeren Body meinst, sind die Member Variablen der Objekte einfach Default-Initialisiert.
  15. Beispiel? Variante mit std::greater<>{}? Dies instanziiert die Spezialisierung std::greater<void>. Dessen operator() Member Funktion ist ebenfalls ein Template und dessen Parameter werden beim Aufruf deduziert. Bei der Instanziierung von std::greater<> selbst findet keine Deduktion statt.
  16. std::begin(*this). Bei std::begin(this) würde eine Iteration ueber ganze Container iterieren, sprich die einzelnen Elemente der Iteration wären Container.

Beispiel 4:

struct Base {
  virtual void foo() = 0;
};

void Base::foo() {
}

struct Derived : Base {
  void foo() {
    Base::foo();
  }
};

int main() {
  Derived d{};
  d.foo();
}

Beispiel 5:

struct S {
  S(int){}
};

void foo(S s) {
}

int main() {
  foo(1);
}


Questions via email by RolfFurrer: ^

  1. What are static and dynamic types? In C++?
  2. Does the keyword virtual affect the function signature? Respectively, is it possible to have a member function with the virtual keyword and without?
  3. What are “Array Functionsparameter”?
  4. What is the difference of a class specialization which is partial or complete in code?
  5. What speaks for making variables or references const in a function header and what not?
  6. Does it makes sense to make default values in functions const?
  7. Which scope will be used if there are several methods in different scopes. Does the using keyword influence this choice? Is there a general rule to it (local, namespace, object)? Or will it just be ambiguous?

For example

int max() {
  return std::numeric_limits<int>::max();
}

int main() {

  // will the following statement influence the precedence?
  using namespace std;
  
  int test = max(); // there is also a max function in std:: , which will be used?
}

  1. What operator takes precedence in the following examples? Can it be said that the operator closest to the variable takes precedence (right and left side)? And also right side over left side? Is there a difference between test4 and test5?

int main() {
  int test = objectInstance.variableX++; // . I assume
  
  int test2 = -++test;  // ++ I assume

  int test3 = ++test2++;
}

  1. When a function is declared or written before the main function, then it has not to be declared in a header file. Is there a use case for this in larger programs?
  2. The <string> include is already in the iostream include. Is that because std::string is already included in the <iostream>? Even if it is already part of it, shouldn’t <string> be included as well?

Antworten von ThomasCorbat: ^

  1. Everything declared is a static type. If a variable has a value type you can be sure that the dynamic type corresponds to the static type. If a variable has a reference or (smart) pointer type, the actual object behind the variable can have a different (dynamic) type. The dynamic type of the object has to be derived from the static type of the declared variable.
  2. No
  3. Parameters that take arrays as arguments. Basically there are two signatures for passing arrays to functions:
  1. I presume the question is about "class template specializations" as there are no class specializations. There is no actual difference, except that the fully specialized template will be instantiated as you see it in the code. The partial specialization still has template parameters that will be substiuted when the compiler creates a template instance.
  2. Declaring a function value parameter const is similar to declaring local variables const. It prevents changing the parameter (or variable) inadvertently. However, declaring reference parameter const is sensible for large types (larger than a pointer) when the callee does not need it own copy. This avoids copying large objects. Declaring the reference parameter const is a guarantee for the caller that the callee may not modify the supplied argument.
  3. Actually, the same reasons as for point 5 (above) are valid.
  4. No max functions has priority over the other in this case. Both can coexist, both are resolved in the given case. However, the compiler will try to resolve the better match when calling the function. The std::max function will be taken unless the user supplied function is a perfect match for the call. If both max implementations were identical (signature) and neither was a template, then we had an ambiguity. The using keyword just adds additional names to consider for lookup, but such names neither have higher nor lower priority.
  5. Precedence and associativity in C++ is specified in the standard and is well summarized on CPPReference. For the cases above (sorry there is no test4 or test 5):
  1. Yes. For helper functions only used within a source file, especially when it is defined in an anonymous namespace, there won't be a declaration required.
  2. iostream includes istream, ostream, ios and streambuf. There is no guarantee that iostream will include string and such a coincidental inclusion may differen from toolchain to toolchain.


|home |print view |recent changes |changed January 22, 2018 |
exact
|You are 54.198.3.15 <- set your identity!

Questions For Exam Preparation HS17
go to start