[interview] C/C + + interview classic II

1. struct

In C

// c
typedef struct Student {
    int age; 
} S;

Equivalent to

// c
struct Student { 
    int age; 
};

typedef struct Student S;

here   S   Equivalent to   struct Student, but the two identifiers have different namespaces.

You can also define and   struct Student   Non conflicting   void Student() {}.

In C + +

Because the compiler's rules for locating symbols (search rules) change, it is different from C language.

1, If defined in class identifier space   struct Student {...};, use   Student me;   When, the compiler searches the global identifier table, student   If not found, search within the class identifier.

That is, it can be used   Student   You can also use   struct Student, as follows:

// cpp
struct Student { 
    int age; 
};

void f( Student me );       // Correct, "struct" keyword can be omitted

2, If and   Student   After a function with the same name   Student   It only represents the function, not the structure, as follows:

typedef struct Student { 
    int age; 
} S;

void Student() {}           // Correct. After definition, "Student" only represents this function

//void S() {} / / error, the symbol "S" has been defined as an alias of "struct Student"

int main() {
    Student(); 
    struct Student me;      // Or "S me";
    return 0;
}

struct and class in C + +

Generally speaking, struct is more suitable to be regarded as the implementation of a data structure, and class is more suitable to be regarded as the implementation of an object.

difference

  • The most essential difference is the default access control
    1. Default inherited access. struct is public and class is private.
    2. As the implementation of data structure, struct's default data access control is public, while class's default member variable access control is private.

2. union

Union is a special class that saves space. A union can have multiple data members, but only one data member can have value at any time. When a member is assigned a value, other members become undefined. The United Nations has the following characteristics:

  • The default access control character is public
  • It can contain constructor and destructor
  • Cannot contain members of reference type
  • Cannot inherit from other classes and cannot be used as a base class
  • Cannot contain virtual functions
  • Anonymous unions can directly access union members in the scope where they are defined
  • An anonymous union cannot contain protected or private members
  • Global anonymous Federation must be static
#include<iostream>

union UnionTest {
    UnionTest() : i(10) {};
    int i;
    double d;
};

static union {
    int i;
    double d;
};

int main() {
    UnionTest u;

    union {
        int i;
        double d;
    };

    std::cout << u.i << std::endl;  // Output 10 of UnionTest Union

    ::i = 20;
    std::cout << ::i << std::endl;  // Output global static anonymous Union

    i = 30;
    std::cout << i << std::endl;    // Output local anonymous Union

    return 0;
}

3. Implement C + + classes in C

C realizes the object-oriented characteristics of C + + (encapsulation, inheritance, polymorphism)

  • Encapsulation: use function pointers to encapsulate attributes and methods into structures
  • Inheritance: structure nesting
  • Polymorphism: the function pointers of parent and child methods are different

Can you write object-oriented code in C? [closed]

4. explicit

  • explicit modifies the constructor to prevent implicit conversion and copy initialization
  • When explicit modifies the conversion function, it can prevent implicit conversion, but   Conversion by context   except
struct A
{
	A(int) { }
	operator bool() const { return true; }
};

struct B
{
	explicit B(int) {}
	explicit operator bool() const { return true; }
};

void doA(A a) {}

void doB(B b) {}

int main()
{
	A a1(1);		// OK: direct initialization
	A a2 = 1;		// OK: copy initialization
	A a3{ 1 };		// OK: direct list initialization
	A a4 = { 1 };		// OK: copy list initialization
	A a5 = (A)1;		// OK: allow static_ Explicit conversion of cast 
	doA(1);			// OK: allow implicit conversion from int to A
	if (a1);		// OK: implicit conversion from A to bool using the conversion function A::operator bool()
	bool a6(a1);		// OK: implicit conversion from A to bool using the conversion function A::operator bool()
	bool a7 = a1;		// OK: implicit conversion from A to bool using the conversion function A::operator bool()
	bool a8 = static_cast<bool>(a1);  // OK : static_ Direct initialization of cast

	B b1(1);		// OK: direct initialization
	B b2 = 1;		// Error: the object decorated with explicit constructor cannot be copied or initialized
	B b3{ 1 };		// OK: direct list initialization
	B b4 = { 1 };		// Error: object decorated with explicit constructor cannot be initialized by copy list
	B b5 = (B)1;		// OK: allow static_ Explicit conversion of cast
	doB(1);			// Error: the object decorated with explicit constructor cannot be implicitly converted from int to B
	if (b1);		// OK: the object of the conversion function B::operator bool() modified by explicit can be converted from B to bool by context
	bool b6(b1);		// OK: the object of the conversion function B::operator bool() modified by explicit can be converted from B to bool by context
	bool b7 = b1;		// Error: the object modified by explicit conversion function B::operator bool() cannot be implicitly converted
	bool b8 = static_cast<bool>(b1);  // OK: static_ Direct initialization of cast

	return 0;
}

5. friend friend class and friend function

  • Access to private members
  • Destroy encapsulation
  • Friend relationship is not transitive
  • Unidirectionality of friend relationship
  • The form and number of friend statements are not limited

6. using

using statement

One   using statement   Statement introduces only one member of a namespace at a time. It allows us to clearly know which name is referenced in the program. For example:

using namespace_name::name;

using declaration of constructor

In C++11, derived classes can reuse constructors defined by their direct base classes.

class Derived : Base {
public:
    using Base::Base;
    /* ... */
};

As stated above, for each constructor of the base class, the compiler generates a derived class constructor corresponding to it (the formal parameter list is exactly the same). Generate the following type constructors:

Derived(parms) : Base(args) { }

using indication

using indication   Make all names visible in a particular namespace so that we don't have to add any prefix qualifiers to them. For example:

using namespace_name name;

Use as little as possible   using indication   Pollution namespace

In general, using the using command is safer than using the compiling command because it imports only the specified name. If the name conflicts with the local name, the compiler issues an indication. The using compile command imports all names, including those that may not be needed. If there is a conflict with the local name, the local name overrides the namespace version and the compiler does not issue a warning. In addition, the openness of namespaces means that the names of namespaces may be scattered in multiple places, which makes it difficult to know exactly which names are added.

Using using

Use as little as possible   using indication

using namespace std;

It should be used more   using statement

int x;
std::cin >> x ;
std::cout << x << std::endl;

perhaps

using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;

*The using keyword in C++11 can realize the function of typedef, and the definition order of using is conducive to searching
*using is more effective than typedef in alias definition of template type

In C/C + +, we often define type aliases through typedef.

typedef unsigned char u1;
typedef unsigned short u2;

However, this definition is a little bad. The newly defined alias is placed later. Generally, we find the original name through the alias. It's still inconvenient to find it from the back to the front.
So, shall we define the alias first?

using u4 = uint32_t;
using u8 = uint64_t;

In C++11, using is no longer just used in using namespace, and has been playing a great role in the field of aliases since then.

using is used to define aliases, especially in the case of templates:
The following example comes from the definition of container type in ART:

template <typename T>
using ArenaDeque = std::deque<T, ArenaAllocatorAdapter<T>>;

template <typename T>
using ArenaQueue = std::queue<T, ArenaDeque<T>>;

template <typename T>
using ArenaVector = std::vector<T, ArenaAllocatorAdapter<T>>;

template <typename T, typename Comparator = std::less<T>>
using ArenaSet = std::set<T, Comparator, ArenaAllocatorAdapter<T>>;

template <typename K, typename V, typename Comparator = std::less<K>>
using ArenaSafeMap =
    SafeMap<K, V, Comparator, ArenaAllocatorAdapter<std::pair<**const** K, V>>>;

With the above definition, our encapsulated types can be used generically:

ArenaVector<MIRLocation> method_stack_;        // Include stack

seven  :: Range resolution operator

  1. Global scope character (:: name): used before the type name (class, class member, member function, variable, etc.), indicating that the scope is a global namespace
  2. Class scope character (class::name): used to indicate that the scope of the specified type is the scope of a specific class
  3. Namespace scope character (namespace::name): used to indicate that the scope of the specified type is specific to a namespace
int count = 11;         // count of global (::)

class A {
public:
	static int count;   // Count of class A (A::count)
};
int A::count = 21;

void fun()
{
	int count = 31;     // The count of the initialization part is 31
	count = 32;         // Set the value of local count to 32
}

int main() {
	::count = 12;       // Test 1: set the global count value to 12

	A::count = 22;      // Test 2: set the count of class A to 22

	fun();		        // Test 3

	return 0;
}

8. enum

Scoped enumeration type

enum class open_modes { input, output, append };

Scopeless enumeration type

enum color { red, yellow, green };
enum { floatPrec = 6, doublePrec = 10 };

9. decltype

decltype keyword is used to check the declaration type of an entity or the type and value classification of an expression. Syntax:

decltype ( expression )
// Tail return allows us to declare the return type after the parameter list
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
    // Processing sequence
    return *beg;    // Returns a reference to an element in a sequence
}
// In order to use template parameter members, you must use typename
template <typename It>
auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type
{
    // Processing sequence
    return *beg;    // Returns a copy of an element in a sequence
}

10. References

lvalue reference

General reference, which generally represents the identity of the object.

rvalue reference

An R-value reference is a reference that must be bound to an R-value (a temporary object and an object to be destroyed). It generally represents the value of an object.

Right value reference can realize move semantics and Perfect Forwarding. Its main purpose is two aspects:

  • Eliminate unnecessary object copies when two objects interact, save computing and storage resources and improve efficiency.
  • It can define generic functions more concisely and clearly.

Reference fold

  • X& &,X& &&,X&& &   Foldable into   X&
  • X&& &&   Foldable into   X&&

11. Macro

  • The macro definition can realize functions similar to functions, but it is not a function after all, and the "parameter" in the bracket in the macro definition is not a real parameter. When the macro is expanded, the "parameter" is replaced one-to-one.

12. Member initialization list

  • More efficient: one less call to the default constructor.
  • In some cases, the initialization list must be used:
    1. Constant members, because constants can only be initialized and cannot be assigned, they must be placed in the initialization list
    2. Reference type. The reference must be initialized at the time of definition and cannot be re assigned, so it should also be written in the initialization list
    3. There is no class type with a default constructor, because using the initialization list you don't have to call the default constructor to initialize

13. initializer_list initialization

#include <iostream>
#include <vector>
#include <initializer_list>
 
template <class T>
struct S {
    std::vector<T> v;
    S(std::initializer_list<T> l) : v(l) {
         std::cout << "constructed with a " << l.size() << "-element list\n";
    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }
    std::pair<const T*, std::size_t> c_arr() const {
        return {&v[0], v.size()};  // Copy list initialization in return statement
                                   // This does not use std::initializer_list
    }
};
 
template <typename T>
void templated_fn(T) {}
 
int main()
{
    S<int> s = {1, 2, 3, 4, 5}; // Replication initialization
    s.append({6, 7, 8});      // List initialization in function call
 
    std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";
 
    for (auto n : s.v)
        std::cout << n << ' ';
    std::cout << '\n';
 
    std::cout << "Range-for over brace-init-list: \n";
 
    for (int x : {-1, -2, -3}) // The rules of auto make this band work
        std::cout << x << ' ';
    std::cout << '\n';
 
    auto al = {10, 11, 12};   // Special rules for auto
 
    std::cout << "The list bound to auto has size() = " << al.size() << '\n';
 
//    templated_fn({1, 2, 3}); //  Compilation error! '{1, 2, 3}' is not an expression,
                             // It has no type, so T cannot be deduced
    templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK
    templated_fn<std::vector<int>>({1, 2, 3});           // Also OK
}

Tags: C C++ Interview

Posted on Fri, 17 Sep 2021 14:24:27 -0400 by sulin