一、 问:重载,重写 ,隐藏区别?
答:
二、 问题:如何解隐藏问题?
答:隐藏分为2个情况,同名函数查找过程 派生类 基类 全局
情况1 如果是通过派生类访问一个函数,派生类局部作用域隐藏上层 base同名函数。
情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。
通过虚函数解决
三、 问 什么是多态?怎么实现的
答:
多态:同一个函数,不同的行为。具体选择那个行为 2个情况。
编译时的多态:函数重载和运算符重载(根据参数不同选择具体函数 )
运行时的多态:通过类继承和虚函数实现的(根据虚表指针 指向 派生类的函数,还是基类的函数)
四、 类型转换有几种情况,有什么区别?
答:
到底什么是多态?这个概念很模糊,不清楚,
就是具体执行那个函数吗?
整理这个文章之后,依然不清楚,有了解的可以告诉我
面向对象的三大特征:
1.封装:保证对象自身数据的完整性、安全性
2.继承:建立类之间的关系,实现代码复用、方便系统的扩展
3.多态:相同的方法调用可实现不同的实现方式。【定义】
多态是指两个或多个属于不同类的对象,对于同一个消息(方法调用)作出不同响应的方式。
、、、、、、、、、
实现多态的方式【为什么3个情况,不是一个情况】
、、、、、、、、、
C++支持两种多态性:编译时多态性,运行时多态性。
1.编译时的多态:函数重载和运算符重载,在编译时就决定调用哪个函数,先期联编 early binding
2.运行时的多态:通过类继承和虚函数实现的
C++运行时多态性是通过虚函数来实现的,
虚函数允许子类重新定义成员函数,
而子类重新定义父类的做法称为覆盖(Override),或者称为重写。
template <class _Container>
class insert_iterator {
protected:
_Container* container;
typename _Container::iterator iter;
public:
operator=(const typename _Container::value_type& __value) {
iter = container->insert(iter, __value);
++iter;
return *this;
}
};
// insert_iterator example
#include <iostream> // std::cout
#include <iterator> // std::insert_iterator
#include <list> // std::list
#include <algorithm> // std::copy
int main () {
std::list<int> foo, bar;
for (int i=1; i<=5; i++)
{ foo.push_back(i); bar.push_back(i*10); }
std::list<int>::iterator it = foo.begin();
advance(it,3);
std::insert_iterator< std::list<int> > insert_it (foo,it);
std::copy (bar.begin(),bar.end(),insert_it);
std::cout << "foo:";
for ( std::list<int>::iterator it = foo.begin(); it!= foo.end(); ++it )
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
Output:
1 2 3 10 20 30 40 50 4 5
多态性机制不仅增加了面向对象软件系统的灵活性,进一步减少了冗余信息, 而且显著提高了软件的可重用性和可扩充性。
从实现的角度来讲,c++多态性可以划分为两类
重载,重写都出现了,隐藏呢:
作用域与名字隐藏机制
数据隐藏
int n = 1;//全局作用域
int main()
{
int n = 2;//局部作用域
{
int n = 3;//块作用域
}
}
转载自《C++ exceptional style》第22条
隐藏经常发生在继承关系中,派生类重新定义了基类的非virtual函数【虚函数也隐藏】,当发生隐藏时,编译器名字隐藏机制如下:
1. 编译器会从当前域开始查找(比如派生类对象调用,会在派生类的定义内查找),查找需要的名字;
2. 如果在当前域没有找到,编译器会在外围作用域继续查找,先是基类的定义内,然后是全局名字空间;
3. 一旦在某个作用域内包含需要的名字就会停下来,并就该作用域内的名字进行决议
,这意味着往外层的作用域就不予考虑了,从而将外层作用域的同名函数隐藏;[不在去寻找更合适的]
4.编译器在当前的名字空间中找到与所求名字同名的实体之间进行决议(函数重载),如果选不出最优,就产生二义性错误;[寻找不到,然后报错]
5. 选出最优的函数,查看函数是否可以访问/调用
IF 子类的函数与父类的名称相同,但是参数不同
父类函数被隐藏
ELSE IF 子类函数与父类函数的名称相同&&参数也相同&&但是父类函数没有virtual
父类函数被隐藏
ELSE IF 子类函数与父类函数的名称相同&&参数也相同&&但是父类函数有virtual
父类函数被覆盖
new 是可以被重载的
https://www.cplusplus.com/reference/new/operator%20new/
throwing (1) void* operator new (std::size_t size);
nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
placement (3)
void* operator new (std::size_t size, void* ptr) noexcept;
// operator new example
#include <iostream> // std::cout
#include <new> // ::operator new
struct MyClass {
int data[100];
MyClass() {std::cout << "constructed [" << this << "]\n";}
};
int main () {
std::cout << "1: ";
MyClass * p1 = new MyClass;
// allocates memory by calling: operator new (sizeof(MyClass))
//1 计算大小
//2 调用 operator new
// and then constructs an object at the newly allocated space
//3 执行构造函数
std::cout << "2: ";
MyClass * p2 = new (std::nothrow) MyClass;
// allocates memory by calling: operator new (sizeof(MyClass),std::nothrow)
// and then constructs an object at the newly allocated space
// 返回值为0 ,不抛异常。
std::cout << "3: ";
new (p2) MyClass;
// does not allocate memory -- calls: operator new (sizeof(MyClass),p2)
// but constructs an object at p2
// Notice though that calling this function directly does not construct an object:
std::cout << "4: ";
MyClass * p3 = (MyClass*) ::operator new (sizeof(MyClass));
// allocates memory by calling: operator new (sizeof(MyClass))
// but does not call MyClass's constructor
return 0;
}
//例子 名字隐藏了new
class Base
{
public:
static void* operator new(std::size_t, const FastMemory&);
};
class Derived: public Base
{
Derived *p1 = new Derived; //错误,无可用的匹配,因为简单new被隐藏
Derived *p2 = new(std::nothrow) Derived; //错误,无可用的匹配,因为nothrow new被隐藏
void *p3 = /* 足够放下一个Derived对象 */
new(p3) Derived; //错误,无可用的匹配,因为定位new被隐藏
FastMemroy f;
Derived *p4 = new(f) Derived; //调用Base::operator new()
}
(1)函数Derived::f(float)覆盖了Base::f(float)。父类函数被覆盖
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。不相同的参数
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。相同的参数
父类函数被隐藏
ELSE IF 子类函数与父类函数的名称相同&&参数也相同&&但是父类函数没有virtual
父类函数被隐藏
ELSE IF 子类函数与父类函数的名称相同&&参数也相同&&但是父类函数有virtual
父类函数被覆盖
#include <iostream>
using namespace std;
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
int main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); //Derived::f(float) 3.14
// 函数Derived::f(float)覆盖了Base::f(float) 多态
pd->f(3.14f); //Derived::f(float) 3.14 非多态
// Bad : behavior depends on type of the pointer
pb->g(3.14f); //Base::g(float) 3.14
pd->g(3.14f); //Derived::g(int) 3
//函数Derived::g(int)隐藏了Base::g(float),不相同的参数
// Bad : behavior depends on type of the pointer
pb->h(3.14f); //Base::h(float) 3.14
pd->h(3.14f); //Derived::h(float) 3.14
// 函数Derived::h(float)隐藏了Base::h(float),相同的参数
return 0;
}
[root@VM-0-10-centos work]# ./a.out
Derived::f(float) 3.14
Derived::f(float) 3.14
Base::g(float) 3.14
Derived::g(int) 3
Base::h(float) 3.14
Derived::h(float) 3.14
错误理解:
情况1 如果是通过派生类访问一个函数,派生类局部作用域隐藏上层 base类函数
情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。
通过虚函数解决
// Good : behavior depends solely on type of the object
pb->f(3.14f); //Derived::f(float) 3.14
// 函数Derived::f(float)覆盖了Base::f(float) 多态
pd->f(3.14f); //Derived::f(float) 3.14 非多态
C.138: Create an overload set for a derived class and its bases with using
原文链接:
C.138: Create an overload set for a derived class and its bases with using
Reason
Without a using declaration, member functions in the derived class hide the entire inherited overload sets.
如果没有using声明,派生类中的成员函数将隐藏整个继承的重载集。
Example, bad
#include <iostream>
class B {
public:
virtual int f(int i) { std::cout << "f(int): "; return i; }
virtual double f(double d) { std::cout << "f(double): "; return d; }
virtual ~B() = default;
};
class D: public B {
public:
int f(int i) override { std::cout << "f(int): "; return i + 1; }
};
int main()
{
D d;
std::cout << d.f(2) << '\n'; // prints "f(int): 3"
std::cout << d.f(2.3) << '\n'; // prints "f(int): 3"
}
//在此证明 虚函数无法解决参数不相同的隐藏问题
Example, good
class D: public B {
public:
int f(int i) override { std::cout << "f(int): "; return i + 1; }
using B::f; // exposes f(double)
};
Note This issue affects both virtual and non-virtual member functions
For variadic bases, C++17 introduced a variadic form of the using-declaration,
template<class... Ts>
struct Overloader : Ts... {
using Ts::operator()...; // exposes operator() from every base
};
Enforcement
Diagnose
name hiding
名称隐藏
C.140: Do not provide different default arguments for a virtual function and an overrider
Reason That can cause confusion: An overrider does not inherit default arguments.Example, bad
class Base {
public:
virtual int multiply(int value, int factor = 2) = 0;
virtual ~Base() = default;
};
class Derived : public Base {
public:
int multiply(int value, int factor = 10) override;
};
Derived d;
Base& b = d;
b.multiply(10); // these two calls will call the same function but
d.multiply(10); // with different arguments and so different results
青铜回答:用override解决隐藏?
点评:
--example 1--
#include<iostream>
using namespace std;
namespace Lib
{
void print(int x)
{
cout<<"int"<<x<<endl;
}
}
void print(double y){
cout<<"double"<<y<<endl;
}
int main()
{
using Lib::print;//example 1 : main作用域中嵌套了Lib命名空间
print(1.3);
print(3);
getchar();
return 1;
}
//输出:
int 1
int 3
问题来源:4.编译器在当前的名字空间中找到与所求名字同名的实体之间进行决议(函数重载),如果选不出最优,就产生二义性错误
https://blog.nowcoder.net/n/bb65a484a87d4a7fab967d0555f6a152
1 int get(int m){
2 return m;
3 }
4
5 long get(long m){
6 return m;
7 }
//double d = 1.234;
//调用get(d);double既可以隐式转换未long,也可以是int,
//或者说一般的数值类型之间都可以进行隐式类型转换,
//故无法确定那一个更加匹配。
C.hier-access: Accessing objects in a hierarchy
Reason
If you have a class with a virtual function,
you don’t (in general) know which class provided the function to be used.
Example
struct B {
int a; virtual int f();
virtual ~B() = default
};
struct D : B { int b; int f() override; };
void use(B b)
{
D d;
B b2 = d; // slice ??? 这地方怎么发生切换了呢
B b3 = b;
}
void use2()
{
D d;
use(d); // slice
}
Both ds are sliced.
Exception
You can safely access a named polymorphic object in the scope of its definition, just don’t slice it.
void use3()
{
D d;
d.f(); // OK
}
See also
A polymorphic class should suppress copying Enforcement Flag all slicing.
C.130: For making deep copies of polymorphic classes prefer a virtual clone function instead of public copy construction/assignment
Reason
Copying a polymorphic class is discouraged due to the slicing problem, see C.67. If you really need copy semantics, copy deeply: Provide a virtual clone function that will copy the actual most-derived type and return an owning pointer to the new object, and then in derived classes return the derived type (use a covariant return type)
.Example
class B {
public:
virtual owner<B*> clone() = 0;
B() = default;
virtual ~B() = default;
B(const B&) = delete; //禁用
B& operator=(const B&) = delete; //禁用
};
class D : public B {
public:
owner<D*> clone() override;
~D() override;
};
Generally, it is recommended to use smart pointers to represent ownership (see R.20). However, because of language rules, the covariant return type cannot be a smart pointer: D::clone can’t return a unique_ptrwhile B::clone returns unique_ptr. Therefore, you either need to consistently return unique_ptr in all overrides, or use owner<> utility from the Guidelines Support Library.
多态:相同的方法调用可实现不同的实现方式,定义 重载 参数不一样呀?怎么算
多态分为四种:重载多态、强制多态、包含多态和参数多态。
重载多态分为两种:函数重载和运算符重载。
可以说,函数重载只是多态这个概念中非常小的一部分。
//求距离 函数相同,参数相同
template <class _InputIterator, class _Distance>
inline void distance(_InputIterator __first,
_InputIterator __last, _Distance& __n)
{
__STL_REQUIRES(_InputIterator, _InputIterator);
__distance(__first, __last, __n, iterator_category(__first));
}
//参数 input_iterator_tag 不同
template <class _InputIterator, class _Distance>
inline void __distance(_InputIterator __first, _InputIterator __last,
_Distance& __n, input_iterator_tag)
{
while (__first != __last) { ++__first; ++__n; }
}
//参数 random_access_iterator_tag 不同
template <class _RandomAccessIterator, class _Distance>
inline void __distance(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Distance& __n, random_access_iterator_tag)
{
__STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
__n += __last - __first;
}
- 类型:
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
- iterator_traits
// The overloaded functions iterator_category, distance_type, and
// value_type are not part of the C++ standard. (They have been
// replaced by struct iterator_traits.) They are included for
// backward compatibility with the HP STL.
// We introduce internal names for these functions.
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{
typedef typename iterator_traits<_Iter>::iterator_category _Category;
return _Category();
}
// 特化版本一,针对原生指针类型
template <class _Tp>
struct iterator_traits<const _Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
template <class _Container>
class insert_iterator {
protected:
_Container* container;
typename _Container::iterator iter;
public:
typedef _Container container_type;
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
一、 问:重载,重写 ,隐藏区别?
答:
二、 问题:如何解隐藏问题?
答:隐藏分为2个情况,同名函数查找过程 派生类 基类 全局
情况1 如果是通过派生类访问一个函数,派生类局部作用域隐藏上层 base同名函数。
情况2 如果是通过通过base类指针或者引用访问 隐藏派生类同名函数。
通过虚函数解决
三、 问 什么是多态?怎么实现的
答:
多态:同一个函数,不同的行为。具体选择那个行为 2个情况。
编译时的多态:函数重载和运算符重载(根据参数不同选择具体函数 )
运行时的多态:通过类继承和虚函数实现的(根据虚表指针 指向 派生类的函数,还是基类的函数)
四、 类型转换有几种情况,有什么区别?
答: