cpp primer笔记110-面向对象程序设计

  1. 如果不想一个类被其他类继承,可以在定义的变量名后面加上final,这样如果其他类继承该类会报错。
class Base final
{
	virtual void func()
	{
		std::cout << "Base" << std::endl;
	}
};
/*class Son :public Base//报错
{
};*/
  1. override可以使得子类的函数不是虚函数的情况下重写父类的虚函数,而基类函数中的final可以使得子类无法重写。如果派生类的重写函数想要调用基类的函数,可以先写出基类的作用域再调用基类的函数,如果派生类对象想用基类而不是子类的同名函数,可以先写成基类的作用域运算符在调用基类函数。
    #define _CRT_SECURE_NO_WARNINGS 1
    #include<iostream>
    #include<map>
    class Base
    {
    public:
    	virtual void func1(int) const
    	{
    
    	}
    	virtual void func2()
    	{
    
    	}
    	void func3() {};
    };
    class Son :public Base
    {
    public:
    	void func1(int) const override//Son的func1和Base的func1匹配
    	{
    		Base::func1(1);
    	}
    	virtual void func2() final
    	{
    
    	}
    	//void func2(int) override{} 错误,应该与Base类的形参列表匹配
    	//void func3() override; 错误,基类中没有声明为func3的虚函数
    	//void func4() override; 错误,基类中没有该函数
    };
    class cla :public Son
    {
    	//void func2() 错误,无法重写基类的函数
    };
    int main()
    {
    	Son s;
    	s.Base::func1(1);
    	return 0;
    }
    
  2. 派生类向基类转换的可访问性
    ![[Pasted image 20230925165218.png]]
	#define _CRT_SECURE_NO_WARNINGS 1
	#include<iostream>
	#include<map>
	#include<memory>
	class Father
	{
	
	};
	class SonA :public Father
	{
		void func(SonA& son)
		{
			Father a = son;
		}
		friend void frifunc(SonA& son)
		{
			Father a = son;
		}
	};
	class SonB :protected Father
	{
		void func(SonB& son)
		{
			Father b = son;
		}
		friend void frifunc(SonB& son)
		{
			Father b = son;
		}
	};
	class SonC :private Father
	{
		void func(SonC& son)
		{
			Father c = son;
		}
		friend void frifunc(SonC& son)
		{
			Father c = son;
		}
	};
	class GrandSonA :SonA
	{
		void func(GrandSonA& son)
		{
			Father a = son;
		}
	};
	class GrandSonB :SonB
	{
		void func(GrandSonB& son)
		{
			Father b = son;
		}
	};
	class GrandSonC:SonC
	{
		void func(GrandSonC& son)
		{
			//Father c = son;不允许私有继承基类派生类的子类转换为基类
		}
	};
	int main()
	{
		std::unique_ptr<Father> faPtr1(new SonA());
		//std::unique_ptr<Father> faPtr2(new SonB()); 
		//如果不在类内部,则不允许保护和私有继承的派生类转换为父类
		//std::unique_ptr<Father> faPtr3(new SonC());
		return 0;
	}
  1. 派生类友元不能访问基类的成员。struct默认继承保护级别是public继承,class继承默认保护级别是private继承
  2. 覆盖重载函数:
    #include <iostream>
    class base {
    public:
    	virtual int func() {return 0;}
    	virtual int func(int i) {return 1;}
    };
    
    class D1 : public base {
    public:
    	int func() { return 2; }
    };
    
    class D2 : public D1 {
    public:
    	int func(int i) { return 3;}
    };
    
    int main()
    {
    	D1 d1;
    	D2 d2;
    	base *b1 = &d1;
    	base *b2 = &d2;
    	b1->func(1);
    	b2->func();	
    	return 0;
    }
    
    
    基类base中有重载函数func的两个版本,D1继承自base,覆盖了func()的版本,D2继承自D1,覆盖了func(int)的版本。
    • 主函数里使用动态绑定,base指针绑定D1对象,可以使用fun(int)版本,同样base指针绑定D2对象,也可以使用fun()版本。
    • 如果在D1调用func(int)版本,比如int funcc() {return func(1);},那么会编译报错,显示no
    • matching function for call to ‘D1::func(int)’。如果改成int funcc() {return func();}就没错了。同理,在D2中使用func()也会报错。
    • 如果直接用d1调用fun(int)版本,或者直接用d2调用func()版本,编译会报错。错误也是no matching function for call to ‘XXX’
    • 如果修改类D1和D2,把覆盖的虚函数去掉,如下所示:
    class D1 : public base {
    public:
    	//int func() { return 2; }
    };
    
    class D2 : public D1 {
    public:
    	//int func(int i) { return 3;}
    };
    
    
    • 派生类的作用域嵌套在基类的作用域之内。 D1只覆盖了无参版本的func函数,那么D1的作用域之内,func(int)版本的函数就被隐藏了。使用b1调用func(int)函数,编译器首先在D1作用域内找名字func,找到了名字func,就不会继续往外层作用域查找,此时参数不匹配,于是编译器报错。
    • 同样,D1中的funcc()函数调用func(int)函数,也是先从D1作用域开始查找名字func,也只能找到无参版本,找到无参版本后发现参数不匹配,于是报错。
    • 而如果使用动态绑定,即使用指针d1访问func(),查找名字将从静态类型的作用域开始,即从base的作用域开始查找,而base的作用域内两个版本的func都可见(其中无参版本的是被覆盖后的,有int参数的是原始的),所以找到名字func后还会进行参数匹配,不会出现编译错误。
    • 修改类D1和D2,把覆盖的虚函数去掉,那么D1和D2作用域内就没有名字func了,使用b1调用func(int)或者func()函数,都无法在D1作用域内找到函数名,于是在外层作用域中继续寻找,也就是在base作用域中查找,能找到重载的两个版本的函数,接着进行正常的函数匹配,也不会出现编译错误。
    • 而书上所说的using声明符,相当于将base作用域里的几个重载版本的函数都强行加到了里层的D1作用域里,也能解决2和3的问题。
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    class base {
    public:
    	virtual int func() { std::cout << 1 << std::endl; return 0; }
    	virtual int func(int i) { return 1; }
    };
    
    class D1 : public base {
    public:
    	using base::func;//如果使用using,则使用子类构造的父类使用父类的所有func函数重载,而不是子类
    	int func() { return 2; }
    };
    
    class D2 : public D1 {
    public:
    	using base::func;//如果添加using,则使用子类构造的父类使用父类所有func函数重载,而不是子类
    	int func(int i) { return 3; }
    };
    
    int main()
    {
    	D1 d1;
    	D2 d2;
    	base* b1 = &d1;
    	base* b2 = &d2;
    	D1().func();//这里使用子类的函数
    	b1->func(1);
    	b2->func();
    	return 0;
    }
    
  3. 如果基类的指针通过派生类构造,如果基类的构造函数不是虚函数,则该指针的(派生类)对象会调用基类的析构函数,因此最好将基类的析构函数用virtual修饰,从而让该指针的对象调用派生类的析构函数。当然定义虚析构函数将不会有默认的移动操作,只能手动添加。
  4. 派生类中删除的拷贝控制与基类的关系
    ![[Pasted image 20230925232959.png]]

![[Pasted image 20230925233315.png]]

  1. 基类容器可通过指针来兼容派生类
    	#define _CRT_SECURE_NO_WARNINGS 1
    	#include <iostream>
    	#include <string>
    	#include <memory>
    	#include <deque>
    	#include <algorithm>
    	class Base
    	{
    	private:
    	    std::string str;
    	    int num;
    	public:
    	    Base(const std::string& s = "No Word", int n = 0) :str(s), num(n) {};
    	    friend std::ostream& operator<<(std::ostream& os, const Base ba)
    	    {
    	        os << ba.str << " " << ba.num;
    	        return os;
    	    }
    	};
    	class SonA :public Base
    	{
    	private:
    	    double val;
    	public:
    	    SonA(const std::string& s = "No Word", int n = 0, double v = 0) :val(v), Base(s, n) {};
    	    friend std::ostream& operator<<(std::ostream& os, const SonA sonA)
    	    {
    	        os << static_cast<const Base&> (sonA) << " " << sonA.val;
    	        return os;
    	    }
    	};
    	class SonB :public Base
    	{
    	private:
    	    char ch;
    	public:
    	    SonB(const std::string& s = "No Word", int n = 0, char c = '\0') :ch(c), Base(s, n) {};
    	    friend std::ostream& operator<<(std::ostream& os, const SonB sonB)
    	    {
    	        os << static_cast<const Base&> (sonB) << " " << sonB.ch;
    	        return os;
    	    }
    	};
    	class GrandSonA :public SonA
    	{
    	private:
    	    long lon;
    	public:
    	    GrandSonA(const std::string& s = "No Word", int n = 0, double v = 0, long l = 0)
    	        :lon(l), SonA(s, n, v) {};
    	    friend std::ostream& operator<<(std::ostream& os, const GrandSonA& grsonA)
    	    {
    	        os << static_cast<const SonA&>(grsonA)<< " " << grsonA.lon;
    	        //可通过强制转换将派生类转换为基类来使用基类的函数
    	        return os;
    	    }
    	};
    	int main() 
    	{
    	    //std::vector<Base> vec1 = { Base("ewr",12),SonA("sdf",32,3) };
    	    //当派生类对象被赋值给基类对象时,其中的派生类部分被切掉
    	    //因此容器和存在继承关系的类型无法兼容
    	    std::deque<std::shared_ptr<Base>> vec2{ std::make_shared<Base>("sdf",234) };
    	    auto ba = std::back_inserter(vec2);
    	    auto fr = std::front_inserter(vec2);
    	    *ba = std::make_shared<SonA>("zfc", 123, 3.4);
    	    //注意,如果派生类继承方式为保护或者私有,则无法添加派生类构造的数据
    	    *fr = std::make_shared<SonB>("ergsdf", 10, '3');
    	    auto ins = std::inserter(vec2, vec2.begin());
    	    *ins = std::make_shared<GrandSonA>("zsdfs", 23, 234.234, 234234234);
    	    std::ostream_iterator<Base> os(std::cout, "\n");
    	    std::for_each(vec2.crbegin(), vec2.crend(), [&os](auto val) { os = *val; });
    	    //打印时,因为全部的是基类,所以每个对象只打印两个值,针对这种行为可以写一组虚函数例如下面的代码
    	    return 0;
    	}
    
    
    
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    class Base
    {
    private:
    	std::string str;
    	int num;
    public:
    	Base(const std::string& s = "No Word", int n = 0) :str(s), num(n) {};
    	virtual std::ostream& SonOstream(std::ostream& os) const
    	{
    		os << this->str << " " << this->num;
    		return os;
    	}
    	friend std::ostream& operator<<(std::ostream& os, const Base ba)
    	{
    		return ba.SonOstream(os);
    	}
    };
    class SonA :public Base
    {
    private:
    	double val;
    public:
    	SonA(const std::string& s = "No Word", int n = 0, double v = 0) :val(v), Base(s, n) {};
    	virtual std::ostream& SonOstream(std::ostream& os) const
    	{
    
    		os << static_cast<const Base&> (SonA(*this)) << " " << this->val;
    		return os;
    	}
    	friend std::ostream& operator<<(std::ostream& os, const SonA sonA)
    	{
    		return sonA.SonOstream(os);
    	}
    };
    class SonB :public Base
    {
    private:
    	char ch;
    public:
    	SonB(const std::string& s = "No Word", int n = 0, char c = '\0') :ch(c), Base(s, n) {};
    	virtual std::ostream& SonOstream(std::ostream& os) const
    	{
    		os << static_cast<const Base&>(SonB(*this)) << " " << this->ch;
    		return os;
    	}
    	friend std::ostream& operator<<(std::ostream& os, const SonB sonB)
    	{
    		return sonB.SonOstream(os);
    	}
    };
    class GrandSonA :public SonA
    {
    private:
    	long lon;
    public:
    	GrandSonA(const std::string& s = "No Word", int n = 0, double v = 0, long l = 0)
    		:lon(l), SonA(s, n, v) {};
    	virtual std::ostream& SonOstream(std::ostream& os) const
    	{
    		os << static_cast<const SonA&>(GrandSonA(*this)) << " " << this->lon;
    		return os;
    	}
    	friend std::ostream& operator<<(std::ostream& os, const GrandSonA& grsonA)
    	{
    		//可通过强制转换将派生类转换为基类来使用基类的函数
    		return grsonA.SonOstream(os);
    	}
    };
    int main()
    {
    	//std::vector<Base> vec1 = { Base("ewr",12),SonA("sdf",32,3) };
    	//当派生类对象被赋值给基类对象时,其中的派生类部分被切掉
    	//因此容器和存在继承关系的类型无法兼容
    	std::deque<std::shared_ptr<Base>> vec2{ std::make_shared<Base>("sdf",234) };
    	auto ba = std::back_inserter(vec2);
    	auto fr = std::front_inserter(vec2);
    	*ba = std::make_shared<SonA>("zfc", 123, 3.4);
    	//注意,如果派生类继承方式为保护或者私有,则无法添加派生类构造的数据
    	*fr = std::make_shared<SonB>("ergsdf", 10, '3');
    	auto ins = std::inserter(vec2, vec2.begin());
    	*ins = std::make_shared<GrandSonA>("zsdfs", 23, 234.234, 234234234);
    	std::ostream_iterator<Base> os(std::cout, "\n");
    	std::for_each(vec2.crbegin(), vec2.crend(), [](std::shared_ptr<Base> val) { val->SonOstream(std::cout); std::cout.put(10); });
    	return 0;
    }
    
  2. 模板运行非类型参数传递。注:函数模板和类模板成员函数的定义通常放在头文件,而非模板函数把声明放在头文件,把定义放在源文件。一个类模板的每一个都形成一个独立的类,例如,类型Blob&ltstring&gtyu任何其他类型比如Blob&ltint&gt都没有任何关联,也不会对任何其他的Blob类型成员有特殊访问权限。
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    template<int N,int M,typename Type>
    bool swap(Type(&t1)[N], Type(&t2)[M])
    {
        if (N != M) return false;
        for (int i = 0; i < N; ++i)
        {
            Type temp = t1[i];
            t1[i] = t2[i];
            t2[i] = temp;
        }
        return true;
    }
    int main() 
    {
        int num1[4]{ 3,4,5,2 }, num2[4]{ 8,7,6,5 };
        swap<4, 4>(num1, num2);
        //swap(num1, num2); 相当于上面的语句,如果上面的语句
        //中N和M都不等于对应的数组长度则或报错,如果是char数组,则需要将N和M加上一
        for (auto x : num1) std::cout << x << " ";
        std::cout << std::endl;
        for (auto y : num2) std::cout << y << " ";
        
        return 0;
    }
    
    
  3. 模板类内使用模板名不需要模板参数,但是模板类外使用模板名来定义类内的成员等操作则需要模板参数
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <vector>
    #include <algorithm>
    template<class T> class BlobPtr
    {
    public:
        BlobPtr func1()//类内部不用写模板参数,只需要模板名
        {
            return BlobPtr();
        }
        BlobPtr func2();
    };
    template<class T>
    BlobPtr<T> BlobPtr<T>::func2()//类外部需要写模板参数和模板名
    {
        return BlobPtr<T>();
    }
    int main() 
    {
        
        return 0;
    }
    
  4. 友元类与类模板
    template <typename T> class C2 {
    // C2的每个实例和相同实例化的pal声明为友元
    friend class Pal<T>;
    //Pal2的所有实例都是C2的每个实例的友元
    template <typename x> friend class Pal2;
    //Pal3是一个非模板类,他是所有C2的友元。
    friend class Pal3;
    };
    
  5. 模板类型可以起别名
	#define _CRT_SECURE_NO_WARNINGS 1
	#include <iostream>
	#include <string>
	#include <memory>
	#include <deque>
	#include <vector>
	#include <algorithm>
	template<typename T> using partNo = std::pair<T, T>;
	template<typename T> using twin = std::pair<T, int>;
	int main() 
	{
	    partNo<int> intPair;//相当于pair<int,int>
	    twin<std::string> strPair;//相当于pair<string,int>
	    return 0;
	}

  1. 如果一个模板参数列表中从右往左有一串连续的模板参数可以通过编译器找到,则在实例化中可以省略指定被找到的模板参数。

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    template<typename T1,typename T2,typename T3>
    T1 sum(T2 num1, T3 num2)
    {
    	return num1 + num2;
    }
    int main()
    {
    	int a = 10;
    	long b = 15;
    	std::cout << sum<long>(a, b);//省略T2和T3
    	return 0;
    }
    
  2. 针对部分需要解除const,引用,指针等修饰的类型cpp定义类标准类型转换模板。 ![[Pasted image 20230926214603.png]]

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    #include <type_traits>
    #include <vector>
    template<typename It>
    auto fcn2(It beg, It end) -> typename std::remove_reference<decltype(*beg)>::type//这里需要写typename,因为beg是通过It模板参数来转换
    {
    	return *beg;
    }
    int main()
    {
    	std::vector<int> vec = { 3,5,21,4 };
    	std::cout << fcn2(vec.begin(), vec.end()) << std::endl;
    	return 0;
    }
    
  3. 如果一个函数有多个重载则函数调用的模板类函数指针的时候,指针需要实例化模板参数

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    template<typename T>
    void swap(const T& t1, const T& t2)
    {
    }
    void func(void (*ptr)(const int&, const int&))
    {
    
    }
    void func(void (*ptr)(const double&, const double&))
    {
    
    }
    int main()
    {
    	//func(swap);如果使用非实例化的函数,
    	//编译器会因为不知道调用哪个func而报错
    	func(swap<int>);
    	return 0;
    }
    
  4. 针对函数传参时候的右值引用传递左值引用,引用折叠可以解决,X& &,X& &&和X&& &都折叠成类型X&,类型X&& &&折叠成X &&

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    template<typename T> void func1(T&){};
    template<typename T> void func2(const T&){};
    template<typename T> void func3(T&& val)
    {
    	T t = val;
    	//当传递一个右值调用Func3时,T是int
    	//且通过拷贝val的值被初始化,当是一个左值时
    	//T是int&
    };
    template<typename T>void func4(T&& val)
    {
    	T t = val;//当val是一个左值,T则是一个左值引用
    	t = [](T& t) {t *= t; return t; }(t);
    	if (val == t)//t是val的一个引用,所以val永远等于t
    	{
    		std::cout << t << std::endl;
    	}
    }
    int main()
    {
    	int i = 10;
    	const int ci = 20;
    	func1(i);//i是一个int;模板参数类型T是int
    	func1(ci);//ci是一个const int;模板参数T是const int
    	//func1(5); 错误:传递给一个&参数的实参必须是左值
    	func2(i);//i是一个int,模板T是一个int
    	func2(ci);//ci是一个const int,但是模板参数T是int
    	func2(5);//一个const &参数可以绑定一个右值;T是int
    	func3(42);//实参是一个int类型的右值,模板参数T是int类型
    	//当传递一个左值给func3的右值引用函数参数是,编译器会推断T为左值类型
    	func3(i);//实参是一个左值,模板参数T是一个int&
    	func3(ci);//实参是一个左值,模板参数T是一个const int&
    	func4(i);
    	return 0;
    }
    
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    template<typename F,typename T1,typename T2>
    void flip1(F f, T1 t1, T2 t2)
    {
    	f(t2, t1);
    }
    template<typename F, typename T1, typename T2>
    void flip2(F f, T1&& t1, T2&& t2)
    //使用右值引用能够保证传入的右值和左值引用参数不会变成值类型
    {
    	f(t2, t1);
    }
    template<typename F, typename T1, typename T2>
    void flip3(F f, T1&& t1, T2&& t2)
    {
    	f(std::forward<T2>(t2), std::forward<T1>(t1));
    	//forward能够将t1和t2的形参保持左值引用,右值引用,
    	//const或者volatile修饰的情况下将将参数转发给函数
    	//相当于针对左右值引用,值类型,const或者volatile之间的类型转换的static_cast,这里的转发t1和t2主要是将T2类型转发为实际对应类型从而在f函数中调用
    }
    void f(int v1, int& v2)
    {
    	std::cout << v1 << " " << ++v2 << std::endl;
    }
    void g(int&& i, int& j)
    {
    	std::cout << i << " " << j << std::endl;
    }
    int main()
    {
    	int i = 5, j = 5;
    	flip1(f, i, 10);
    	f(10, j);
    	std::cout << i << " " << j << std::endl;
    	i = 5, j = 5;
    	flip2(f, i, 10);
    	f(10, j);
    	std::cout << i << " " << j << std::endl;
    	//flip2(g, i, 10);10是一个常量,传入过程中变为右值引用
    	flip3(g, i, 10);
    	std::cout << i << " " << j << std::endl;
    	return 0;
    }
    
    10 6
    10 6
    5 6
    10 6
    10 6
    6 6
    10 6
    6 6
    
  5. 可变参数模板

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <deque>
    #include <algorithm>
    #include <cstdarg>
    template<typename T,typename... Args>
    void func(const T& t, const Args&... rest)
    {
    	std::cout << "Args大小 " << sizeof ...(Args) << std::endl;
    	//可变参数模板参数大小
    	std::cout << "rest大小 " << sizeof ...(rest) << std::endl;
    	//可变参数模板参数大小
    }
    template<typename T>
    std::ostream& print(std::ostream& os,const T &t)
    {
    	return os << t;
    }
    template<typename T,typename... Args>
    std::ostream& print(std::ostream& os, const T& t, const Args&... rest)
    {
    	os << t << ", ";
    	return print(os, rest...);
    }
    //print(cout,1,2,3,4,5);
    //print(cout,1,2,3,4);
    //print(cout,1,2,3);
    //print(cout,1,2);
    //print(cout,1)调用非可变参数版本的print
    int main()
    {
    	const int i = 10;
    	func(1, 2, 3, 4, 5, 6);
    	func(2, 2.234, 0ull, "Sdfsdf", std::string("234"));
    	print(std::cout, 1, 2, 3, 4, 5, 6);
    	std::cout.put(10);
    	print(std::cout, 2, 2.234, 0ul, "Sdfsdf", std::string("234"));
    	return 0;
    }
    
  6. 模板类的成员函数显式具体化

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <iostream>
    #include <string>
    #include <memory>
    #include <vector>
    #include <algorithm>
    template<typename T> class Foo
    {
    	Foo(const T& t = T()) :mem(t) {};
    	void Bar() { std::cout << 1 << std::endl; };
    	T mem;
    };
    template < >
    void Foo<int>::Bar()
    {
    	std::cout << 2 << std::endl;
    }
    int main()
    {
    	
    	return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值