C++备忘录
参考资料:
1. 《C++编程思想(第一卷)》
知识点:
● register变量:它是一种局部变量类型。它告诉编译器尽快访问该变量。一般会把变量放在寄存器中,但不保证一定会。不能得到或计算寄存器变量的地址,且寄存器变量必须在模块中声明。无全局或静态寄存器变量。
注意:要相信编译器,尽量避免使用register关键字。
● static变量:①方法内部的静态变量在该方法首次调用时被初始化,并在以后的方法调用时保存该静态变量的值(记忆的效果)。
②当一个静态方法或变量在所有方法外时,意味着该变量只能在该文件中能被访问。也就说它的作用域是该文件,即使加了extern关键字。
● static成员变量既可以在构造函数中初始化,也可以在类外初始化。
● extern关键字:extern关键字告诉编译器该变量或方法存在,尽管在当前被编译文件中没有被看到。而该变量或方法实际上可能定义在其它文件中或当前文件的下文。再次强调,extern和static不能共存。
● volatile关键字:volatile修饰符则告诉编译器“你永远不知道它什么时候改变”,并阻止编译器进行基于该变量的稳定性的优化。主要是多线程环境下。强迫读,强迫写,从而保证数据一致。
● 内部链接internal linkage和外部链接external linkage。
● cast强制类型转换:cast告诉编译器不要进行类型检查,并把该变量当做期望的类型。
● struct无成员的时候,长度为1Byte(每一个对象都必须拥有一个唯一地址)。有时候,不会将函数和其它包含在Struct的长度中,只计成员变量。C++中结构体可用访问控制符:public、protected和private。
#ifndef STACK_H #define STACK_H ////code here #endif
● 不要在头文件中使用directives(using xxxxx),因为这样会失去对该namespace命名空间的保护(在CPP文件中使用)。Don't put using directives in header files.
● friend关键字的意义就是打破访问控制符的作用,从而也打破了封装。嵌套类或结构体也需要通过friend关键字才能访问外部类的私有成员。结构体所有的访问控制符都会在程序运行之前消失(仅仅是内存区域),且发生在编译阶段。
struct X; struct Y { void f(X*); }; struct X { private: int i; public: void inialize(); friend void g(X*, int); // 全局友元 friend void Y::f(X*); // 成员友元 friend struct Z; // 整个结构体都是友元 friend void h(); }; void X::inialize() { i = 0; } void g(X* x, int i) { x->i = i; } void Y::f(X* x) { x->i = 47; } struct Z { private: int j; public: void intialize(); void g(X* x); }; void Z::intialize() { j = 99; } void Z::g(X* x) { x->i += j; } void h() { X x; x.i = 100; }
● class和struct在C++中基本是等同的,除了class默认为private,而struct默认为public。
● 最好自定义构造方法,而不是让编译器去做这件事。
● union可以由构造方法和析构方法。
union U { private: int i; float f; public: U(int a); U(float b); ~U(); int read_int(); float read_float(); }; U::U(int a) { i = a; } U::U(float b) { f = b; } U::~U() { cout << "Destructor of U\n"; } int U::read_int() { return i; } float U::read_float() {return f; } class SuperVar { enum { character, integer, floating_point } vartype; union { char c; int i; float f; }; public: SuperVar(char ch); SuperVar(int ii); SuperVar(float ff); void print(); }; SuperVar::SuperVar(char ch) { vartype = character; c = ch; } SuperVar::SuperVar(int ii) { vartype = integer; i = ii; } SuperVar::SuperVar(float ff) { vartype = floating_point; f = ff; } void SuperVar::print() { switch(vartype) { case character: cout << "character: " << c << endl; break; case integer: cout << "integer: " << i << endl; break; case floating_point: cout << "float: " << f << endl; break; } }
● const关键字:const修饰符就是告诉编译器“它永远不会被改变”。
● 类中的const成员变量的意思是:在该对象的生命周期中,该常数一直存在。每一个对象可能包含不同的常数。类中的static const的意思是:该类的所有对象,都共享同一个常数(运行时常数)。
● 类中的const函数的意思是:它不会改变类中成员变量的值。
● 成员函数的const参数的意思是:该参数不会在该函数中被改变。
● const成员变量的周期是每一个类对象,它只能在类的构造函数中初始化(C++ 11后不是了)。要想定义类的常量,需要使用枚举enum { var = 20};该枚举不占用类对象的空间,且在编译时被全部求值。
class Widget { const int i; // const int i = 10; //C++ 11以后支持 static int j; public: Widget(int s) : i(s) { j = 20; } }; int Widget::j = 30;
● mutable关键字将bitwise的const对象中的成员变得可以被修改(logic const)。
class Z { int i; mutable int j; public: Z(); void f() const; }; Z::Z() : i(0), j(0){} void Z::f() const { j++; } int main() { const Z zz; zz.f(); return 0; }
● ROMability(往ROM里写)需要满足的条件:①首先该对象必须是bitwise的const对象;②class或struct必须没有用户自定义的构造方法和析构方法;③基类或成员对象必须没有用户自定义的构造方法和析构方法。
● 类中的inline函数直到该类声明结束时(大括号结束时)才会被评估。
class Forward { int i; public: Forward() : i(1) {} int f() const { return g() + i; } int g() const { return i; } };
● C++引用C的函数(“C”) || C引用C++的函数(“C++”)。 => _func1_int_char || _func1
extern "C" float func1(int a, char b); extern "C" { float func1(int a, char b); double func2(int a, char b); } extern "C" { #include "Test.h" }
● 引用reference(&)有点类似于常数指针,能被自动解引用dereferenced。使用引用的一些规则:①当引用被创建时,必须被初始化;②一旦引用被初始化给一个对象,它就不能被改变去引用另一个对象;③不能有NULL引用,引用必须与合法的内存关联。
● Unary一元运算符重载:成员方法和全局方法。
成员方法
class Byte { unsigned char b; public: Byte(unsigned char bb = 0) : b(bb) {} const Byte& operator + () const { return *this; } const Byte operator - () const { return Byte(-b); } const Byte operator ~ () const { return Byte(~b); } Byte operator ! () const { return Byte(!b); } Byte* operator& () { return this; } const Byte& operator ++ () { b++; return *this; } const Byte operator ++ (int) { Byte before(b); ++b; return before; } const Byte& operator -- () { b--; return *this; } const Byte operator -- (int) { Byte before(b); --b; return before; } };
全局方法
class Integer { long i; Integer* This() { return this; } public: Integer(long ll = 0) : i(ll) {} friend const Integer& operator + (const Integer& a); friend const Integer& operator - (const Integer& a); friend const Integer operator ~ (const Integer& a); friend Integer* operator & (Integer& a); friend int operator ! (const Integer& a); friend const Integer& operator ++ (Integer& a); // Prefix friend const Integer operator ++ (Integer& a, int); // Postfix friend const Integer& operator -- (Integer& a); // Prefix friend const Integer operator -- (Integer& a, int); // Postfix void print() { cout << i << endl; } }; const Integer& operator + (const Integer& a) { return a; } const Integer& operator - (const Integer& a) { return Integer(-a.i); } const Integer operator ~ (const Integer& a) { return Integer(~a.i); } Integer* operator & (Integer& a) { return a.This(); } int operator ! (const Integer& a) { return !a.i; } const Integer& operator ++ (Integer& a) { a.i++; return a; } const Integer operator ++ (Integer& a, int) { Integer before(a.i); ++a.i; return before; } const Integer& operator -- (Integer& a) { a.i--; return a; } const Integer operator -- (Integer& a, int) { Integer before(a.i); --a.i; return before; }
● Binary二元运算符重载:成员方法和全局方法。
注意:=只能是成员方法。
成员方法
class Byte { unsigned char b; public: Byte(unsigned char bb = 0) : b(bb) {} const Byte operator + (const Byte& right) const { return Byte(b + right.b); } const Byte operator - (const Byte& right) const { return Byte(b - right.b); } const Byte operator * (const Byte& right) const { return Byte(b * right.b); } const Byte operator / (const Byte& right) const { return Byte(b / right.b); } const Byte operator % (const Byte& right) const { return Byte(b % right.b); } const Byte operator ^ (const Byte& right) const { return Byte(b ^ right.b); } const Byte operator & (const Byte& right) const { return Byte(b & right.b); } const Byte operator | (const Byte& right) const { return Byte(b | right.b); } const Byte operator << (const Byte& right) const { return Byte(b << right.b); } const Byte operator >> (const Byte& right) const { return Byte(b >> right.b); } Byte& operator = (const Byte& right) { if (this == &right) return *this; b = right.b; return *this; } Byte& operator += (const Byte& right) { if (this == &right) { char temp = right.b; b += temp; } else { b += right.b; } return *this; } Byte& operator -= (const Byte& right) { if (this == &right) { char temp = right.b; b -= temp; } else { b -= right.b; } return *this; } Byte& operator *= (const Byte& right) { if (this == &right) { char temp = right.b; b *= temp; } else { b *= right.b; } return *this; } Byte& operator /= (const Byte& right) { if (this == &right) { char temp = right.b; b /= temp; } else { b /= right.b; } return *this; } Byte& operator %= (const Byte& right) { if (this == &right) { char temp = right.b; b %= temp; } else { b %= right.b; } return *this; } Byte& operator ^= (const Byte& right) { if (this == &right) { char temp = right.b; b ^= temp; } else { b ^= right.b; } return *this; } Byte& operator &= (const Byte& right) { if (this == &right) { char temp = right.b; b &= temp; } else { b &= right.b; } return *this; } Byte& operator |= (const Byte& right) { if (this == &right) { char temp = right.b; b |= temp; } else { b |= right.b; } return *this; } Byte& operator >>= (const Byte& right) { if (this == &right) { char temp = right.b; b >>= temp; } else { b >>= right.b; } return *this; } Byte& operator <<= (const Byte& right) { if (this == &right) { char temp = right.b; b <<= temp; } else { b <<= right.b; } return *this; } int operator == (const Byte& right) const { return b == right.b; } int operator != (const Byte& right) const { return b != right.b; } int operator < (const Byte& right) const { return b < right.b; } int operator > (const Byte& right) const { return b > right.b; } int operator <= (const Byte& right) const { return b <= right.b; } int operator >= (const Byte& right) const { return b >= right.b; } int operator && (const Byte& right) const { return b && right.b; } int operator || (const Byte& right) const { return b || right.b; } void print() const { cout << hex << int(b) << endl; } };
全局方法
class Integer { long i; Integer* This() { return this; } public: Integer(long ll = 0) : i(ll) {} friend const Integer operator + (const Integer& left, const Integer& right); friend const Integer operator - (const Integer& left, const Integer& right); friend const Integer operator * (const Integer& left, const Integer& right); friend const Integer operator / (const Integer& left, const Integer& right); friend const Integer operator % (const Integer& left, const Integer& right); friend const Integer operator ^ (const Integer& left, const Integer& right); friend const Integer operator & (const Integer& left, const Integer& right); friend const Integer operator | (const Integer& left, const Integer& right); friend const Integer operator << (const Integer& left, const Integer& right); friend const Integer operator >> (const Integer& left, const Integer& right); friend Integer& operator += (Integer& left, const Integer& right); friend Integer& operator -= (Integer& left, const Integer& right); friend Integer& operator *= (Integer& left, const Integer& right); friend Integer& operator /= (Integer& left, const Integer& right); friend Integer& operator %= (Integer& left, const Integer& right); friend Integer& operator ^= (Integer& left, const Integer& right); friend Integer& operator &= (Integer& left, const Integer& right); friend Integer& operator |= (Integer& left, const Integer& right); friend Integer& operator <<= (Integer& left, const Integer& right); friend Integer& operator >>= (Integer& left, const Integer& right); friend int operator == (const Integer& left, const Integer& right); friend int operator != (const Integer& left, const Integer& right); friend int operator < (const Integer& left, const Integer& right); friend int operator > (const Integer& left, const Integer& right); friend int operator <= (const Integer& left, const Integer& right); friend int operator >= (const Integer& left, const Integer& right); friend int operator && (const Integer& left, const Integer& right); friend int operator || (const Integer& left, const Integer& right); void print() const { cout << i << endl; } }; const Integer operator + (const Integer& left, const Integer& right) { return Integer(left.i + right.i); } const Integer operator - (const Integer& left, const Integer& right) { return Integer(left.i - right.i); } const Integer operator * (const Integer& left, const Integer& right) { return Integer(left.i * right.i); } const Integer operator / (const Integer& left, const Integer& right) { return Integer(left.i / right.i); } const Integer operator % (const Integer& left, const Integer& right) { return Integer(left.i % right.i); } const Integer operator ^ (const Integer& left, const Integer& right) { return Integer(left.i ^ right.i); } const Integer operator & (const Integer& left, const Integer& right) { return Integer(left.i & right.i); } const Integer operator | (const Integer& left, const Integer& right) { return Integer(left.i | right.i); } const Integer operator << (const Integer& left, const Integer& right) { return Integer(left.i << right.i); } const Integer operator >> (const Integer& left, const Integer& right) { return Integer(left.i >> right.i); } Integer& operator += (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i += temp; } else { left.i += right.i; } return left; } Integer& operator -= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i -= temp; } else { left.i -= right.i; } return left; } Integer& operator *= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i *= temp; } else { left.i *= right.i; } return left; } Integer& operator /= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i /= temp; } else { left.i /= right.i; } return left; } Integer& operator %= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i %= temp; } else { left.i %= right.i; } return left; } Integer& operator ^= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i ^= temp; } else { left.i ^= right.i; } return left; } Integer& operator &= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i &= temp; } else { left.i &= right.i; } return left; } Integer& operator |= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i |= temp; } else { left.i |= right.i; } return left; } Integer& operator >>= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i >>= temp; } else { left.i >>= right.i; } return left; } Integer& operator <<= (Integer& left, const Integer& right) { if (&left == &right) { long temp = right.i; left.i <<= temp; } else { left.i <<= right.i; } return left; } int operator == (const Integer& left, const Integer& right) { return left.i == right.i; } int operator != (const Integer& left, const Integer& right) { return left.i != right.i; } int operator < (const Integer& left, const Integer& right) { return left.i < right.i; } int operator > (const Integer& left, const Integer& right) { return left.i > right.i; } int operator <= (const Integer& left, const Integer& right) { return left.i <= right.i; } int operator >= (const Integer& left, const Integer& right) { return left.i >= right.i; } int operator && (const Integer& left, const Integer& right) { return left.i && right.i; } int operator || (const Integer& left, const Integer& right) { return left.i || right.i; }
● 重载逗号运算符
#include <iostream> using namespace std; class After { public: const After& operator, (const After&) const { cout << "After::operator, ()" << endl; return *this; } }; class Before {}; Before& operator, (int, Before& b) { cout << "Before::operator, ()" << endl; return b; } int main() { After a,b; a, b; Before c; 2, c; return 0; }
● 重载->运算符
#include <iostream> #include <vector> using namespace std; class Obj { static int i, j; public: void f() { cout << i++ << endl; } void g() { cout << j++ << endl; } }; int Obj::i = 47; int Obj::j = 11; class ObjContainer { vector<Obj*> a; public: void add(Obj* obj) { a.push_back(obj); } class SmartPointer; friend SmartPointer; class SmartPointer { ObjContainer& oc; unsigned int index; public: SmartPointer(ObjContainer& objc) : oc(objc), index(0) {} bool operator++() { if (index >= oc.a.size()) return false; if (oc.a[++index] == 0) return false; return true; } bool operator++(int) { return operator++(); } Obj* operator->() const { return oc.a[index]; } }; SmartPointer begin() { return SmartPointer(*this); } }; int main() { const int sz = 10; Obj o[sz]; ObjContainer oc; for (int i = 0; i < sz; i++) oc.add(&o[i]); ObjContainer::SmartPointer sp = oc.begin(); do { sp->f(); sp->g(); } while(++sp); return 0; }
● 重载[ ]运算符
class IntArray { enum { sz = 5}; int localArray[sz]; public: IntArray() { memset(localArray, 0, sz * sizeof(*localArray)); } int& operator[] (int index) { return localArray[index]; } };
● 重载->*运算符(pointer-to-member dereference operator),用以模拟built-in pointer-to-member。
#include <iostream> using namespace std; class Dog { public: int run(int i) const { cout << "run...\n"; return i; } int eat(int i) const { cout << "eat...\n"; return i; } int sleep(int i) const { cout << "ZZZ...\n"; return i; } typedef int (Dog::*PMF)(int) const; class FunctionObject { Dog* ptr; PMF pmem; public: FunctionObject(Dog* wp, PMF pmf) : ptr(wp), pmem(pmf) { cout << "FunctionObject constructor\n"; } int operator()(int i) const { cout << "FunctionObject::operator()\n"; return (ptr->*pmem)(i); } }; FunctionObject operator->*(PMF pmf) { cout << "operator->*" << endl; return FunctionObject(this, pmf); } }; int main() { Dog w; Dog::PMF pmf = &Dog::run; cout << (w->*pmf)(1) << endl; pmf = &Dog::sleep; cout << (w->*pmf)(2) << endl; pmf = &Dog::eat; cout << (w->*pmf)(3) << endl; return 0; }
● 不可重载的运算符有:作用域操作符:: ,条件运算符? : , 点(成员)操作符.以及.*, 预处理符#
● explicit关键字与自动类型转化
#include <iostream> using namespace std; class One { public: One() {} }; class Two { public: //Two(const One&) {} explicit Two(const One&) {} }; class Three { int i; public: Three(int ii = 0, int = 0) : i(ii){} }; class Four { int x; public: Four(int xx) : x(xx){} operator Three() const { return Three(x); } }; int main() { One one; //Two two = one; Four four(1); Three three = four; return 0; }
● 重载全局 new & delete
#include <cstdlib> #include <cstdio> using namespace std; void* operator new(size_t sz) { void* m = malloc(sz); if (!m) puts("out of memory"); return m; } void operator delete(void* m) { puts("operator delete"); free(m); } class S { int i[100]; public: S() { puts("S::S()"); } ~S() { puts("S::~S()"); } }; int main() { puts("creating & deleting an int"); int* p = new int[47]; delete p; puts("\ncreating & deleting an s"); S* s = new S(); delete s; puts("\ncreating & deleting S[3]"); S* sa = new S[3]; delete []sa; return 0; }
结果:
● 在类中重载new & delete
#include <iostream> #include <cstddef> #include <fstream> #include <new> using namespace std; ofstream out("Framis.out"); class Framis { enum { sz = 10 }; char c[sz]; static unsigned char pool[]; static bool alloc_map[]; public: enum { psize = 100 }; Framis() { out << "Framis()\n"; } ~Framis() { out << "~Framis()... "; } void* operator new(size_t) throw(bad_alloc); void operator delete(void*); }; unsigned char Framis::pool[psize * sizeof(Framis)]; bool Framis::alloc_map[psize] = { false }; void* Framis::operator new(size_t) throw(bad_alloc) { for (int i = 0; i < psize; i++) { if (!alloc_map[i]) { out << "using block " << i << " ... "; alloc_map[i] = true; return pool + (i * sizeof(Framis)); } } out << "out of memory" << endl; throw bad_alloc(); } void Framis::operator delete(void* m) { if(!m) return; unsigned long block = (unsigned long)m - (unsigned long)pool; block /= sizeof(Framis); out << "freeing block " << block << endl; alloc_map[block] = false; } int main() { Framis* f[Framis::psize]; try { for (int i = 0; i < Framis::psize; i++) f[i] = new Framis; new Framis; } catch (bad_alloc) { cerr << "Out of memory!" << endl; } delete f[10]; f[10] = 0; Framis* x = new Framis(); delete x; for (int j = 0; j < Framis::psize; j++) { delete f[j]; } return 0; }
● 为数组重载new & delete1,
#include <new> #include <fstream> using namespace std; ofstream trace("ArrayOperatorNew.out"); class Widget { enum { sz = 10}; int i[sz]; public: Widget() { trace << "*"; } ~Widget() { trace << "~"; } void* operator new(size_t sz) { trace << "Widget::new: " << sz << " bytes" << endl; return ::new char[sz]; } void operator delete(void* p) { trace << "Widget::delete" << endl; ::delete []p; } void* operator new[](size_t sz) { trace << "Widget::new[]: " << sz << " bytes" << endl; return ::new char[sz]; } void operator delete[](void* p) { trace << "Widget::delete[]" << endl; ::delete []p; } }; int main() { trace << "new Widget" << endl; Widget* w = new Widget; trace << "\ndelete Widget" << endl; delete w; trace << "\ndelete Widget[25]" << endl; Widget* wa = new Widget[25]; trace << "\ndelete []Widget" << endl; delete []wa; return 0; }
结果:
● new一个的对象的过程:先分内存,再调用构造函数。如果分配内存失败,抛出bad_alloc异常,且构造函数不会被调用。更多请参考:C++ new失败处理
● 构造函数初始化列表(constructor initializer list)对应着继承行为,都有冒号这种类似的结构。std::initializer_list<T>(C++11)类型的对象是一个轻量级的代理对象,里面的成员都是常量。 如:auto a = { 1, 2, 3 }。
● 构造函数和析构函数调用顺序:构造函数从父类开始,到最末端子类结束;析构函数调用过程与之相反。在初始化列表中的成员变量总是在父类构造函数被调用之后被调用,且按照在类中定义的顺序初始化;析构时,成员变量总是在析构函数执行后,按照在类中定义的顺序逆向释放空间。
测试代码:
#include <iostream> using namespace std; #define CLASS(ID) class ID {\ public:\ ID(int) { cout << #ID " constructor\n"; }\ ~ID() { cout << #ID " destructor\n"; }\ }; CLASS(BaseClass); CLASS(MemberOne); CLASS(MemberTwo); CLASS(MemberThree); CLASS(MemberFour); class SubClass : public BaseClass { MemberOne m1; MemberTwo m2; public: SubClass(int) : m2(2), m1(1), BaseClass(0) { cout << "SubClass constructor\n"; } ~SubClass() { cout << "SubClass destructor\n"; } }; class SubSubClass : public SubClass { MemberThree m3; MemberFour m4; public: SubSubClass() : m3(3), SubClass(-1), m4(4) { cout << "SubSubClass constructor\n"; } ~SubSubClass() { cout << "SubSubClass destructor\n"; } }; int main() { SubSubClass ssc; return 0; }
结果:
● 在C/C++中,#的功能是将其后边的宏参数进行字符串化操作,而##则被称为连接符,用来将两个子串Token连接为一个Token。
#include <cstdio> #define lookup(x, format) printf(#x " = %" #format "\n", x) #define lookupAgain(i) lookup(x##i, d) using namespace std; int main() { int i = 1; char* s = "hello world"; float x = 2.0; lookup(i, d); // equal to printf("i = %d\n", i) lookup(x, f); // equal to printf("x = %f\n", x) lookup(s, s); // equal to printf("s = %s\n", s) printf("\n"); int x1 = 1, x2 = 2, x3 = 3; lookupAgain(1); // equal to lookup(x1, d) lookupAgain(2); // equal to lookup(x2, d) lookupAgain(3); // equal to lookup(x3, d) return 0; }
结果:
● 私有继承与私有组件的区别在于,私有继承可以部分公共化父类的接口。
#include <iostream> using namespace std; class Animal { public: void eat() const { cout << "I am eating..." << endl; } void speak() const { cout << "I am speaking..." << endl; } void sleep() const { cout << "I am sleeping..." << endl; } void sleep(int time) { cout << "I will sleep " << time << " minutes..." << endl;} }; class Lion : Animal { public: using Animal::eat; using Animal::sleep; // Both sleep }; int main() { Lion lion; lion.eat(); lion.sleep(); lion.sleep(20); return 0; }
● 连接方法调用到方法体即绑定(binding)。绑定=前期绑定+后期绑定(动态绑定或运行时绑定)。C++ 通过vpointer指向的VTABLE实现的,关键字是virtual。具体参见C++对象模型的研究。
● 在构造函数中调用虚函数,会调用当前类中的函数(就近原则),但可以通过Base::Method()的方式调用基类的函数。
构造函数不能使用virtual修饰,单析构函数必须使用virtual修饰。纯析构函数必须由方法体(类外定义)。
纯虚函数的使用:
#include <cassert> using namespace std; class Pet { public: virtual void speak() const = 0; virtual void eat() const = 0; virtual void sleep() const = 0; virtual string getName () const { return "I am a pet"; }; }; void Pet::speak() const { cout << "Pet::speak()" << endl; } void Pet::eat() const { cout << "Pet::eat()" << endl; } class Dog : public Pet { public: void speak() const { Pet::speak(); } void eat() const { cout << "Dog::eat()" << endl; } void sleep() const { cout << "Dog::sleep()" << endl; } }; int main() { Dog dog; dog.speak(); dog.eat(); dog.sleep(); cout << dog.getName() << endl; return 0; }