All C++ Interview Questions
What is the difference between std::string and a char array in C++?
| Point | std::string | char array |
|---|---|---|
| Size | Grows by itself | Fixed |
| Join | a + b | strcat (can overflow) |
| Compare | a == b | strcmp |
| Length | size() | strlen |
| Safety | Safe | Buffer overflow risk |
#include <cstring>
#include <iostream>
#include <string>
int main() {
std::string s = "Hello";
s += " World";
std::cout << s << ' ' << s.size() << '\n';
char c[] = "Hello";
std::cout << sizeof(c) << ' ' << std::strlen(c) << '\n'; // 6 bytes (with \0), 5 characters
}
Hello World 11
6 5
See C++ Strings.
What is the STL in C++?
The STL has three main parts:
- Containers:
vector,list,map,set,unordered_map,stack,queue. - Iterators: objects which point to the items of a container.
- Algorithms:
sort,find,count,transform,accumulateand many more.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {4, 1, 3};
std::sort(v.begin(), v.end());
for (int x : v) std::cout << x << ' ';
std::cout << '\n';
}
1 3 4
See STL Containers and Algorithms.
What is the auto keyword in C++?
#include <iostream>
#include <map>
#include <string>
#include <vector>
int main() {
auto n = 5; // int
auto d = 2.5; // double
auto name = std::string("C++");
std::vector<std::map<std::string, int>> data = {{{"a", 1}}};
for (const auto& m : data) { // no long type name
std::cout << m.size() << '\n';
}
std::cout << n << ' ' << d << ' ' << name << '\n';
}
1
5 2.5 C++
autoneeds an initial value.autoremovesconstand references: useconst auto&when you need them.
See C++ Variables.
What is the difference between const and constexpr in C++?
| Point | const | constexpr |
|---|---|---|
| Meaning | Read only after the start | Computed at compile time |
| Value known | Can be at run time | Must be at compile time |
| Use for array sizes | Only if the value is a compile time constant | Yes |
#include <array>
#include <iostream>
constexpr int square(int n) { return n * n; }
int main() {
constexpr int size = square(3); // calculated while compiling
std::array<int, size> a{};
const int input = 5; // const, set at the start
std::cout << a.size() << ' ' << input << '\n';
}
9 5
See C++ Variables.
What is the static keyword in C++?
- Static local variable: keeps its value between calls.
- Static data member: one copy for the whole class.
- Static member function: can be called without an object, and has no
this. - Static at file level: the name is visible only in that file.
#include <iostream>
int next_id() {
static int id = 0; // set only once
return ++id;
}
struct Counter {
static inline int total = 0;
Counter() { total++; }
};
int main() {
std::cout << next_id() << next_id() << next_id() << '\n';
Counter a, b;
std::cout << Counter::total << '\n';
}
123
2
See Static Members.
What are access specifiers in C++?
| Specifier | Who can use it |
|---|---|
| public | Everyone |
| protected | The class and derived classes |
| private | Only the class (and friends) |
class A {
private:
int secret = 1;
public:
int open = 2;
};
int main() {
A a;
int x = a.open;
int y = a.secret;
}
Private.cpp: In function 'int main()':
Private.cpp:11:15: error: 'int A::secret' is private within this context
11 | int y = a.secret;
| ^~~~~~
Private.cpp:3:9: note: declared private here
3 | int secret = 1;
| ^~~~~~
Private.cpp:10:9: warning: unused variable 'x' [-Wunused-variable]
10 | int x = a.open;
| ^
Private.cpp:11:9: warning: unused variable 'y' [-Wunused-variable]
11 | int y = a.secret;
| ^
What is exception handling in C++?
Use throw to report an error and try ... catch to handle it.
#include <iostream>
#include <stdexcept>
int main() {
try {
throw std::runtime_error("something went wrong");
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << '\n';
}
}
Caught: something went wrong
See C++ Exceptions.
How to compile and run a C++ program?
#include <iostream>
int main() {
std::cout << "Hello, C++!\n";
}
$ g++ -std=c++23 -Wall -Wextra hello.cpp -o hello
$ ./hello
Hello, C++!
The steps inside are: preprocessing, compiling, assembling and linking. See C++ Installation and Setup.
What is RAII in C++?
RAII (Resource Acquisition Is Initialization) ties the life of a resource (memory, file, lock, socket) to the life of an object. The constructor takes the resource, and the destructor releases it. When the object leaves its scope, C++ calls the destructor, even if an exception is thrown.
#include <iostream>
#include <mutex>
#include <stdexcept>
std::mutex m;
void risky() {
std::lock_guard<std::mutex> lock(m); // RAII: locks here
std::cout << "locked\n";
throw std::runtime_error("oops"); // the lock is still released
}
int main() {
try {
risky();
} catch (const std::exception& e) {
std::cout << "caught: " << e.what() << '\n';
}
std::cout << "can lock again: " << m.try_lock() << '\n';
}
locked
caught: oops
can lock again: 1
Standard RAII types: std::unique_ptr, std::vector, std::string, std::lock_guard and std::ofstream. See Constructors and Destructors.
What is the Rule of Three, Five and Zero in C++?
| Rule | Meaning |
|---|---|
| Rule of Zero | Do not write special member functions. Let std::string, vector and smart pointers handle the resources |
| Rule of Three | If you need a custom destructor, copy constructor or copy assignment, you probably need all three |
| Rule of Five | Add the move constructor and move assignment to the three |
#include <iostream>
#include <string>
#include <vector>
struct Person { // Rule of Zero: no special functions written
std::string name;
std::vector<int> scores;
};
int main() {
Person a{"Riya", {80, 90}};
Person b = a; // a correct copy, made by the compiler
Person c = std::move(a); // a correct move
std::cout << b.name << ' ' << b.scores.size() << ' ' << c.name << ' ' << a.name.empty() << '\n';
}
Riya 2 Riya 1
What does std::move do in C++?
std::move(x) is only a cast to T&&. The real moving is done by the move constructor or move assignment. After a move, the old object is valid but its value is unspecified (usually empty).
#include <iostream>
#include <string>
#include <utility>
int main() {
std::string a(30, 'x');
std::string b = a; // copy: both have the text
std::string c = std::move(a); // move: c takes the buffer
std::cout << a.size() << ' ' << b.size() << ' ' << c.size() << '\n';
}
0 30 30
Do not use std::move on a returned local variable: return x; already moves, and a manual std::move can stop the compiler from skipping the copy completely (copy elision). See Move Semantics.
What is the difference between unique_ptr, shared_ptr and weak_ptr?
| Type | Owners | Copy | Cost |
|---|---|---|---|
| unique_ptr | One | No (move only) | None |
| shared_ptr | Many | Yes | Reference count (atomic) |
| weak_ptr | None | Yes | Small |
#include <iostream>
#include <memory>
int main() {
auto u = std::make_unique<int>(1);
auto v = std::move(u); // move the ownership
auto s1 = std::make_shared<int>(2);
std::weak_ptr<int> w = s1; // does not increase the count
auto s2 = s1;
std::cout << (u == nullptr) << ' ' << s1.use_count() << ' ' << w.use_count() << '\n';
s1.reset();
s2.reset(); // the last owner is gone
std::cout << w.expired() << '\n';
std::cout << (w.lock() == nullptr) << '\n';
}
1 2 2
1
1
Start with unique_ptr. Use shared_ptr only when ownership is really shared. weak_ptr breaks cycles, where two shared objects point to each other. See Smart Pointers.
Why do we need a virtual destructor in C++?
If you delete a derived object through a base pointer and the base destructor is not virtual, only the base destructor runs. This is undefined behavior, and the resources of the derived class leak.
#include <iostream>
#include <memory>
struct Base {
virtual ~Base() { std::cout << "~Base\n"; }
};
struct Derived : Base {
~Derived() override { std::cout << "~Derived\n"; }
};
int main() {
std::unique_ptr<Base> p = std::make_unique<Derived>();
} // both destructors run, because ~Base is virtual
~Derived
~Base
Rule: a class which is used as a base class for polymorphism must have a virtual destructor (or a protected non virtual one). See Polymorphism.
How do virtual functions work in C++ (vtable and vptr)?
The compiler makes one vtable for each class which has virtual functions. It is a table of function pointers. Each object gets a hidden pointer, the vptr, to the vtable of its real class. A virtual call follows the vptr to find the right function at run time.
You can see the cost of the hidden pointer in the size of the objects:
#include <iostream>
struct Plain { int x; };
struct Virtual { int x; virtual void f() {} };
int main() {
std::cout << sizeof(Plain) << ' ' << sizeof(Virtual) << '\n';
}
4 16
On a 64 bit system the virtual class is bigger by the size of a pointer (and padded). A virtual call costs a little more than a normal call, and the compiler can often not put it inline. See Virtual Functions.
What is object slicing in C++?
#include <iostream>
struct Base {
virtual const char* who() const { return "Base"; }
};
struct Derived : Base {
int extra = 42;
const char* who() const override { return "Derived"; }
};
int main() {
Derived d;
Base sliced = d; // copies only the Base part
const Base& ref = d; // no copy
std::cout << sliced.who() << ' ' << ref.who() << '\n';
}
Base Derived
Pass polymorphic objects by reference, pointer or smart pointer, and store pointers in containers (std::vector<std::unique_ptr<Base>>), not objects. See Polymorphism.
What is const correctness in C++?
- const variables:
const int max = 10; - const reference parameters: read without a copy.
- const methods: promise not to change the object (
int size() const). - const pointers:
const int*(value const) andint* const(pointer const).
#include <iostream>
#include <string>
class User {
public:
explicit User(std::string n) : name(std::move(n)) {}
const std::string& get_name() const { return name; } // does not change the object
void rename(std::string n) { name = std::move(n); } // changes it: not const
private:
std::string name;
};
void print(const User& u) { // can only call const methods
std::cout << u.get_name() << '\n';
// u.rename("x"); // error if enabled
}
int main() {
User u("Riya");
print(u);
}
Riya
Also the keyword mutable lets a member change inside a const method (used for caches and mutexes). See Classes.
What is undefined behavior in C++? Give examples.
Undefined behavior (UB) is code for which the standard gives no rules. The compiler may assume it never happens, so the result is anything: a crash, a wrong answer, or “it works on my machine”.
- Reading a variable which is not initialized.
- An array index out of range, or dereferencing
nullptr. - Signed integer overflow (unsigned overflow is defined: it wraps).
- Using an object after it is deleted (dangling pointer).
- Two threads changing the same data without a lock (data race).
- Modifying a
constobject, or a missing return in a function which returns a value.
Tools find UB while testing. We compile this code with the undefined behavior sanitizer:
#include <climits>
#include <iostream>
int main() {
int x = INT_MAX;
x = x + 1; // signed overflow: undefined behavior
std::cout << x << '\n';
}
$ g++ -std=c++23 -fsanitize=undefined ub.cpp -o ub
$ ./ub 2>&1 | sed 's/^[^ ]*ub.cpp/ub.cpp/'
ub.cpp:6:7: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
-2147483648
Use -fsanitize=address,undefined and -Wall -Wextra in every test build.
What is the difference between std::map and std::unordered_map?
| Point | std::map | std::unordered_map |
|---|---|---|
| Inside | Balanced tree | Hash table |
| Order | Sorted by key | No order |
| Find, insert | O(log n) | O(1) on average |
| Key needs | operator< | hash and == |
| Iterators | Stay valid after insert | Can be invalidated by a rehash |
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
int main() {
std::map<std::string, int> sorted{{"pear", 3}, {"apple", 1}, {"mango", 2}};
for (const auto& [k, v] : sorted) std::cout << k << ' '; // always sorted
std::cout << '\n';
std::unordered_map<std::string, int> fast{{"pear", 3}, {"apple", 1}};
std::cout << fast.at("pear") << ' ' << fast.contains("kiwi") << '\n';
}
apple mango pear
3 0
Choose unordered_map when you only look up by key. Choose map when you need the order or range searches. See STL Containers.
What is the difference between push_back and emplace_back in C++?
#include <iostream>
#include <string>
#include <vector>
struct Item {
Item(std::string n, int q) : name(std::move(n)), qty(q) { std::cout << "construct " << name << '\n'; }
Item(const Item& o) : name(o.name), qty(o.qty) { std::cout << "copy " << name << '\n'; }
Item(Item&& o) noexcept : name(std::move(o.name)), qty(o.qty) { std::cout << "move " << name << '\n'; }
std::string name;
int qty;
};
int main() {
std::vector<Item> v;
v.reserve(4); // avoid moves caused by growing
v.push_back(Item("pen", 1)); // builds a temporary, then moves it
v.emplace_back("book", 2); // builds directly in the vector
}
construct pen
move pen
construct book
emplace_back avoids one move. For simple types the difference is small, and push_back is fine. See Vectors.
What is iterator invalidation in C++?
A change to a container can make old iterators, pointers and references invalid. Using them is undefined behavior. For a vector, push_back can move all the items to a bigger memory block when the capacity is full.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
const int* before = v.data();
v.reserve(1000); // the data moves to a new place
const int* after = v.data();
std::cout << "data moved: " << (before != after) << '\n';
// an iterator or pointer taken before reserve() must not be used now
}
data moved: 1
| Container | After insert | After erase |
|---|---|---|
| vector | All invalid if it reallocates, else from the insert point | From the erased item onward |
| list, map, set | Stay valid | Only the erased item is invalid |
| unordered_map | Can all become invalid on a rehash | Only the erased item |
Safe habits: use indexes, call reserve early, and do not keep iterators across changes. See Vectors.
- « Previous
- 1
- 2
- 3
- Next »