...
Code Block |
---|
|
#include <iostream>
#include <string>
class Employee {
std::string Namename;
protected:
virtual void print(std::ostream &OSos) const {
OSos << "Employee: " << getNameget_name() << std::endl;
}
public:
Employee(const std::string &Namename) : Namename(Namename) {}
const std::string &getNameget_name() const { return Namename; }
friend std::ostream &operator<<(std::ostream &OSos, const Employee &Ee) {
Ee.print(OSos);
return OSos;
}
};
class Manager : public Employee {
Employee Assistantassistant;
protected:
void print(std::ostream &OSos) const override {
OSos << "Manager: " << getNameget_name() << std::endl;
OSos << "Assistant: " << std::endl << "\t" << getAssistantget_assistant() << std::endl;
}
public:
Manager(const std::string &Namename, const Employee &Assistantassistant) : Employee(Namename), Assistantassistant(Assistantassistant) {}
const Employee &getAssistantget_assistant() const { return Assistantassistant; }
};
void f(Employee Ee) {
std::cout << Ee;
}
int main() {
Employee Codercoder("Joe Smith");
Employee Typisttypist("Bill Jones");
Manager Designerdesigner("Jane Doe", Typisttypist);
f(Codercoder);
f(Typisttypist);
f(Designerdesigner);
}
|
When f()
is called with the Designer
designer
argument, the formal parameter in f()
is sliced and information is lost. When the Employee
object is printed, Employee::Printprint()
is called instead of Manager::Printprint()
, resulting in the following output:
...
Code Block |
---|
|
void f(const Employee *Ee) {
if (Ee) {
std::cout << *Ee;
}
}
int main() {
Employee Codercoder("Joe Smith");
Employee Typisttypist("Bill Jones");
Manager Designerdesigner("Jane Doe", Typisttypist);
f(&Codercoder);
f(&Typisttypist);
f(&Designerdesigner);
}
|
This compliant solution also complies with EXP34-C. Do not dereference null pointers in the implementation of f()
. With this definition, the output becomes
...
Code Block |
---|
|
void f(const Employee &Ee) {
std::cout << Ee;
}
int main() {
Employee Codercoder("Joe Smith");
Employee Typisttypist("Bill Jones");
Manager Designerdesigner("Jane Doe", Typisttypist);
f(Codercoder);
f(Typisttypist);
f(Designerdesigner);
} |
Compliant Solution (Noncopyable)
...
Code Block |
---|
|
#include <iostream>
#include <string>
class Noncopyable {
Noncopyable(const Noncopyable &) = delete;
void operator=(const Noncopyable &) = delete;
protected:
Noncopyable() = default;
};
class Employee : Noncopyable {
std::string Namename;
protected:
virtual void print(std::ostream &OSos) const {
OSos << "Employee: " << getNameget_name() << std::endl;
}
public:
Employee(const std::string &Namename) : Namename(Namename) {}
const std::string &getNameget_name() const { return Namename; }
friend std::ostream &operator<<(std::ostream &OSos, const Employee &Ee) {
Ee.print(OSos);
return OSos;
}
};
class Manager : public Employee {
const Employee &Assistantassistant; // Note: This definition has been modified
protected:
void print(std::ostream &OSos) const override {
OSos << "Manager: " << getNameget_name() << std::endl;
OSos << "Assistant: " << std::endl << "\t" << getAssistantget_assistant() << std::endl;
}
public:
Manager(const std::string &Namename, const Employee &Assistantassistant) : Employee(Namename), Assistantassistant(Assistantassistant) {}
const Employee &getAssistantget_assistant() const { return Assistantassistant; }
};
// If f() were declared as accepting an Employee, the program would be
// ill-formed because Employee cannot be copy-initialized.
void f(const Employee &Ee) {
std::cout << Ee;
}
int main() {
Employee Codercoder("Joe Smith");
Employee Typisttypist("Bill Jones");
Manager Designerdesigner("Jane Doe", Typisttypist);
f(Codercoder);
f(Typisttypist);
f(Designerdesigner);
} |
Noncompliant Code Example
...
Code Block |
---|
|
// In addition to the #includes from the previous example
#include <vector>
void f(const std::vector<Employee> &Vv) {
for (const auto &Ee : Vv) {
std::cout << Ee;
}
}
int main() {
Employee Typisttypist("Joe Smith");
std::vector<Employee> Vv{Typisttypist, Employee("Bill Jones"), Manager("Jane Doe", Typisttypist)};
f(Vv);
} |
Compliant Solution
This compliant solution stores std::unique_ptr
smart pointers in the std::vector
, which eliminates the slicing problem:
Code Block |
---|
|
// In addition to the #includes from the previous example
#include <memory>
#include <vector>
void f(const std::vector<std::unique_ptr<Employee>> &Vv) {
for (const auto &Ee : Vv) {
std::cout << *Ee;
}
}
int main() {
std::vector<std::unique_ptr<Employee>> Vv;
Vv.emplace_back(new Employee("Joe Smith"));
Vv.emplace_back(new Employee("Bill Jones"));
Vv.emplace_back(new Manager("Jane Doe", *Vv.front()));
f(Vv);
} |
Risk Assessment
Slicing results in information loss, which could lead to abnormal program execution or denial-of-service attacks.
...