C++ Primer第五版_第十九章习题答案(1~10)

news/2024/12/29 19:01:54/

文章目录

      • 练习19.1
      • 练习19.2
      • 练习19.3
      • 练习19.4
      • 练习19.5
      • 练习19.6
      • 练习19.7
      • 练习19.8
      • 练习19.9
      • 练习19.10

练习19.1

使用 malloc 编写你自己的 operator new(sizt_t)函数,使用 free 编写operator delete(void *)函数。

#include <cstdlib>
#include <new>void *operator new(size_t size)
{if(void *mem = malloc(size))return mem;elsethrow std::bad_alloc();
}void operator delete(void *mem) noexcept { free(mem); }int main()
{return 0;
}

练习19.2

默认情况下,allocator 类使用 operator new 获取存储空间,然后使用 operator delete 释放它。利用上一题中的两个函数重新编译并运行你的 StrVec 程序。

#ifndef STRVEC_H_
#define STRVEC_H_#include <string>
#include <utility>
#include <memory>
#include <algorithm>
#include <iostream>void *operator new(size_t size)
{std::cout << "void *operator new(size_t size)" << std::endl;if(void *mem = malloc(size))return mem;elsethrow std::bad_alloc();
}void operator delete(void *mem) noexcept
{std::cout << "void operator delete(void *mem) noexcept" << std::endl;free(mem);
}class StrVec
{
friend bool operator==(StrVec &lhs, StrVec &rhs);
friend bool operator!=(StrVec &lhs, StrVec &rhs);
friend bool operator<(StrVec &lhs, StrVec &rhs);
friend bool operator>(StrVec &lhs, StrVec &rhs);
friend bool operator<=(StrVec &lhs, StrVec &rhs);
friend bool operator>=(StrVec &lhs, StrVec &rhs);
public:StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) { }StrVec(std::initializer_list<std::string>);StrVec(const StrVec&);StrVec(StrVec &&s) noexcept : alloc(std::move(s.alloc)), elements(std::move(s.elements)), first_free(std::move(s.first_free)), cap(std::move(s.cap)) { s.elements = s.first_free = s.cap = nullptr; }template <typename... Args>void emplace_back(Args&&... args);StrVec &operator=(const StrVec&);StrVec &operator=(StrVec&&) noexcept;std::string& operator[](std::size_t n) { return elements[n]; }const std::string& operator[](std::size_t n) const { return elements[n]; }~StrVec();void push_back(const std::string&);size_t size() const { return first_free - elements; }size_t capacity() const { return cap - elements; }std::string *begin() const { return elements; }std::string *end() const { return first_free; }void reserve(size_t n);void resize(size_t n);void resize(size_t n, const std::string &s);
private:std::allocator<std::string> alloc;void chk_n_alloc() { if(size() == capacity()) reallocate(); }std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);void free();void reallocate();std::string *elements;std::string *first_free;std::string *cap;
};StrVec::StrVec(std::initializer_list<std::string> il)
{auto newdata = alloc_n_copy(il.begin(), il.end());elements = newdata.first;first_free = cap = newdata.second;
}template <typename... Args>
inline void StrVec::emplace_back(Args&&... args)
{chk_n_alloc();alloc.construct(first_free++, std::forward<Args>(args)...);
}void StrVec::push_back(const std::string &s)
{chk_n_alloc();alloc.construct(first_free++, s);
}std::pair<std::string*,std::string*> StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{auto data = alloc.allocate(e-b);return {data, uninitialized_copy(b, e, data)};
}void StrVec::free()
{if(elements){std::for_each(elements, first_free, [this](std::string &p){ alloc.destroy(&p); });// for(auto p = first_free; p != elements; )// 	alloc.destroy(--p);alloc.deallocate(elements, cap-elements);}
}StrVec::StrVec(const StrVec &s)
{auto newdata = alloc_n_copy(s.begin(), s.end());elements = newdata.first;first_free = cap = newdata.second;
}StrVec::~StrVec()
{free();
}void StrVec::reserve(size_t n)
{if(n > capacity()) return;auto newdata = alloc.allocate(n);auto dest = newdata;auto elem = elements;for(size_t i = 0; i != size(); ++i)alloc.construct(dest++, std::move(*elem++));free();elements = newdata;first_free = dest;cap = elements + n;
}void StrVec::resize(size_t n)
{resize(n,std::string());
}void StrVec::resize(size_t n, const std::string &s)
{if(n < size()){while(n < size())alloc.destroy(--first_free);}else if(n > size()){while(n > size())push_back(s);// alloc.construct(first_free, s);}
}StrVec &StrVec::operator=(const StrVec &rhs)
{auto data = alloc_n_copy(rhs.begin(), rhs.end());free();elements = data.first;first_free = cap = data.second;return *this;
}StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{if(this != &rhs){free();alloc = std::move(rhs.alloc);elements = std::move(rhs.elements);first_free = std::move(rhs.first_free);cap = std::move(rhs.cap);rhs.elements = rhs.first_free = rhs.cap = nullptr;}return *this;
}void StrVec::reallocate()
{auto newcapacity = size() ? 2 * size() : 1;auto newdata = alloc.allocate(newcapacity);auto dest = newdata;auto elem = elements;for(size_t i = 0; i != size(); ++i)alloc.construct(dest++, std::move(*elem++));free();elements = newdata;first_free = dest;cap = elements + newcapacity;
}bool operator==(StrVec &lhs, StrVec &rhs)
{return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
}bool operator!=(StrVec &lhs, StrVec &rhs)
{return !(lhs == rhs);
}bool operator<(StrVec &lhs, StrVec &rhs)
{return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}bool operator>(StrVec &lhs, StrVec &rhs)
{return rhs < lhs;
}bool operator<=(StrVec &lhs, StrVec &rhs)
{return !(rhs < lhs);
}bool operator>=(StrVec &lhs, StrVec &rhs)
{return !(lhs < rhs);
}#endif

练习19.3

已知存在如下的类继承体系,其中每个类分别定义了一个公有的默认构造函数和一个析构函数:

class A { /* ... */};
class B : public A { /* ... */};
class C : public B { /* ... */};
class D : public B, public A { /* ... */};

下面哪个 dynamic_cast 将失败?

(a) A *pa = new C;B *pb = dynamic_cast<B*>(pa);
(b) B *pb = new B;C *pc = dynamic_cast<C*>(pb);
(c) A *pa = new D;B *pb = dynamic_cast<B*>(pa);

(a)成功;
(b)失败,pb是指向B的指针,不能转换为指向C的指针;
(c)失败,A *pa = new D具有二义性。

练习19.4

使用上一个练习定义的类改写下面的代码,将表达式*pa 转换成类型C&:

if (C *pc = dynamic_cast<C*>(pa))
{//使用C的成员
} else {//使用A的成员
}#include <typeinfo>
#include <iostream>class A
{
public:virtual ~A() {}
};class B : public A
{};class C : public B
{};class D : public B, public A
{};int main(int argc, char const *argv[])
{// A *pa = new C;// B *pb = dynamic_cast<B*>(pa);// if(pb) std::cout << "success" << std::endl;// else std::cout << "fail" << std::endl;// B *pb = new B;// C *pc = dynamic_cast<C*>(pb);// if(pc) std::cout << "success" << std::endl;// else std::cout << "fail" << std::endl;// A *pa = new D;// B *pb = dynamic_cast<B*>(pa);// if(pb) std::cout << "success" << std::endl;// else std::cout << "fail" << std::endl;A *pa = new C;try{const C &c = dynamic_cast<const C&>(*pa);}catch(std::bad_cast &e){std::cout << e.what() << std::endl;}return 0;
}

练习19.5

在什么情况下你应该用 dynamic_cast 替代虚函数?

我们想使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数,则可以使用RTTI运算符(该类类型应该含有虚函数)。

练习19.6

编写一条表达式将 Query_base 指针动态转换为 AndQuery 指针。分别使用 AndQuery 的对象以及其他类型的对象测试转换是否有效。打印一条表示类型转换是否成功的信息,确保实际输出的结果与期望的一致。

Query_base为抽象虚类,AndQuery的构造函数为private,暂时没想到方法来实现该题,目前使用19.3的继承体系来验证。

#include <typeinfo>
#include <iostream>class A
{
public:virtual ~A() {}
};class B : public A
{};class C : public B
{};class D : public B, public A
{};int main(int argc, char const *argv[])
{A *pa1 = new C;if(C *qc = dynamic_cast<C*>(pa1)){std::cout << "success" << std::endl;}else{std::cout << "fail" << std::endl;}A *pa2 = new C;try{const C &rc = dynamic_cast<const C&>(*pa2);}catch(std::bad_cast &e){std::cout << e.what() << std::endl;}C c = C();if(typeid(*pa1) == typeid(*pa2)) std::cout << "same type" << std::endl;if(typeid(*pa1) == typeid(c)) std::cout << "same type as C" << std::endl;if(typeid(*pa1) == typeid(C)) std::cout << "same type as C" << std::endl;return 0;
}

练习19.7

编写与上一个练习类似的转换,这一次将 Query_base 对象转换为 AndQuery 的引用。重复上面的测试过程,确保转换能正常工作。

练习19.8

编写一条 typeid 表达式检查两个 Query_base 对象是否指向同一种类型。再检查该类型是否是 AndQuery。

练习19.9

编写与本节最后一个程序类似的代码,令其打印你的编译器为一些常见类型所起的名字。如果你得到的输出结果与本书类似,尝试编写一个函数将这些字符串翻译成人们更容易读懂的形式。

#include <iostream>
#include <typeinfo>class Base
{friend bool operator==(const Base&, const Base&);
public:Base() = default;Base(int i_) : i(i_) { }
protected:virtual bool equal(const Base&) const;
private:int i;
};class Derived : public Base
{
public:Derived() = default;Derived(std::string s_, int i_) : s(s_), Base(i_) { }
protected:bool equal(const Base&) const;
private:std::string s;
};bool operator==(const Base &lhs, const Base &rhs)
{return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}bool Base::equal(const Base &rhs) const
{return this->i == rhs.i;
}bool Derived::equal(const Base &rhs) const
{auto r = dynamic_cast<const Derived&>(rhs);return (this->s == r.s) && this->Base::equal(rhs);
}int main()
{Base *pb1 = new Derived();Base *pb2 = new Derived();Base *pb3 = new Derived("a", 1);Base *pb4 = new Base();std::cout << std::boolalpha << (*pb1 == *pb2) << std::endl;std::cout << std::boolalpha << (*pb1 == *pb3) << std::endl;std::cout << std::boolalpha << (*pb1 == *pb4) << std::endl;int arr[10];Derived d;std::cout << typeid(42).name() << ", "<< typeid(arr).name() << ", "<< typeid(d).name() << ", "<< typeid(std::string).name() << ", "<< typeid(pb1).name() << ", "<< typeid(*pb1).name() << ", "<< typeid(*pb3).name() << std::endl;return 0;
}$ ./ex09 true
false
false
i, A10_i, 7Derived, Ss, P4Base, 7Derived, 7Derived

练习19.10

已知存在如下的类继承体系,其中每个类定义了一个默认公有的构造函数和一个虚析构函数。下面的语句将打印哪些类型名字?

class A { /* ... */ };
class B : public A { /* ... */ };
class C : public B { /*...*/ };
(a) A *pa = new C;cout << typeid(pa).name() << endl;
(b) C cobj;A& ra = cobj;cout << typeid(&ra).name() << endl;
(c) B *px = new B;A& ra = *px;cout << typeid(ra).name() << endl;

(a)P1A;
(b)P1A;
(c)1B。

#include <typeinfo>
#include <iostream>class A
{
public:virtual ~A() {}
};class B : public A
{};class C : public B
{};int main(int argc, char const *argv[])
{// A *pa = new C;// std::cout << typeid(pa).name() << std::endl;// C cobj;// A &ra = cobj;// std::cout << typeid(&ra).name() << std::endl;B *px = new B;A &ra = *px;std::cout << typeid(ra).name() << std::endl;return 0;
}

http://www.ppmy.cn/news/98594.html

相关文章

【C++】Map、Set 模拟实现

文章目录 &#x1f4d5; 概念&#x1f4d5; 实现框架Find()★ 迭代器 ★反向迭代器map 的 operator[ ] &#x1f4d5; 源代码rb_tree.hset.hmap.h &#x1f4d5; 概念 map、set 是 C 中的关联式容器&#xff0c;由于 map 和set所开放的各种操作接口&#xff0c;RB-tree 也都提…

SpringBoot2-核心技术(一)

SpringBoot2-核心技术&#xff08;一&#xff09; 了解SpringBoot配置文件的使用 文章目录 SpringBoot2-核心技术&#xff08;一&#xff09;了解SpringBoot配置文件的使用一、文件类型1. properties2. yaml 二、yaml的基本使用1. 基本语法2. 数据类型2.1 字面量 2.2 对象2.3 …

HashMap,HashTable,ConcurrentHashMap的区别

a、HashMap是非线程安全的&#xff0c;HashTable是线程安全的。   b、HashMap的键和值都允许有null值存在&#xff0c;而HashTable则不行。   c、因为线程安全的问题&#xff0c;HashMap效率比HashTable的要高。   HashMap&#xff1a;它根据键的hashCode值存储数据&a…

HCIA-MSTP替代技术之设备堆叠

1.什么是堆叠、集群 堆叠&#xff1a;将多个具有堆叠特性的交换设备逻辑上变成一台设备&#xff0c;作为一个整体参与转发 集群&#xff1a;将两个具有集群特性的交换设备组合为逻辑上的一台设备。 集群支持两台设备&#xff08;css&#xff09;&#xff0c;一般框式交换机支…

k8s介绍

目录 1&#xff1a;k8s概念 2&#xff1a;为什么引入k8s和k8s特性 2.1 为什么要引入k8s&#xff1a; 2.2 k8s特性 3 K8S架构 1&#xff1a;k8s概念 k8s官方网站&#xff1a;Kubernetes Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和…

网页端扫码通过公众号实现微信授权登录

1.参考开发文档&#xff1a; https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#02.先调起微信授权页面&#xff0c;获取code。&#xff08;如果用户同意授权&#xff0c;页面将跳转至 redirect_uri/?codeCODE&stateSTAT…

【五】设计模式~~~创建型模式~~~单例模式(Java)

【学习难度&#xff1a;★☆☆☆☆&#xff0c;使用频率&#xff1a;★★★★☆】 5.1. 模式动机 对于系统中的某些类来说&#xff0c;只有一个实例很重要&#xff0c;例如&#xff0c;一个系统中可以存在多个打印任务&#xff0c;但是只能有一个正在工作的任务&#xff1b;一…

五种IO模型

本文分享的是理解五种IO模型的基本概念, 重点是IO多路转接。 什么是IO 其实我们在读写数据的时候&#xff0c;比如使用了write、read、recv、send等等函数&#xff0c;本质是对数据的拷贝。即在写的时候&#xff0c;将数据拷贝到了TCP协议的发送缓冲区中。在读的时候&#xff…