c++面向对象 梭哈
c++ 面向对象学习笔记
代码格式
头文件的写法
防卫式声明 gurad
ifndef __COMPLEX__ #这个名次是自己取的
#define __COMPLEX__
. #前置声明
. #类-声明
. #类-定义
#endif
标准库中基于对象的两个典型案例
类中不带指针(复数complex)
#include
using namespace std;// 同一个class的各个object(对象)互为友元 可以直接用另一个对象的pricvate的数据
class complex
{
public: //公开complex(double r = 0, double i = 0): re(r), im(i){ }complex& operator += (const complex&);
// const 指这个函数不能改变对象里面的数据 传入的参数加const 指不能改变参数的数值
// const 放在类的前面是常对象 只能调用类的 const 成员(包括 const 成员变量和 const 成员函数)double real () const { return re; } double imag () const { return im; } private:double re,im;
// 声明一个全局函数的友元friend complex& __doapl ( complex*, const complex& );
};inline complex&
__doapl (complex* ths, const complex& r)
{ths->re += r.re;ths->im += r.im;return *ths;
}inline complex&
complex::operator += (const complex& r)
{return __doapl (this, r);
}// cout 重载只能是作为友元函数,或者是全局函数,因为成员函数的调用是需要由这个对象引发的,而重载的符号只能作用在它前一个类型上,
// 也就是说当作为成员函数的时候必须要是 obj<<cout ,这样才能告诉编译器去成员函数里面去找重载类型,显然这样写是十分反人类的
ostream&
operator << (ostream& os, const complex& x)
{return os << '(' << x.real() << ',' << x.imag() << ')';
}int main()
{complex c(1,2);cout << c;return 0;
}
类中带有指针的(字符串string)
#include
#include
using namespace std;
#ifndef __MYSTRING__
#define __MASTRING__ class String
{
public:String(const char* cstr = 0);String(const String &str);~String( );String& operator = (const String& str);char * get_c_str() const { return m_date; }
private:char * m_date;
};#endifinline
String::String(const char* cstr = 0)
{if (cstr) {m_date = new char [ strlen(cstr)+1];strcpy(m_date, cstr);}else {m_date = new char [1];* m_date = '\0';}
}inline
String::~String()
{delete[] m_date;
}inline
String::String(const String &str)// string a(b) c++新创建的东西会自动调用构造函数 string a = b 一样
{m_date = new char [strlen(str.m_date)+ 1];strcpy(m_date, str.m_date);
}inline
String & String::operator= (const String & str)
{if (this == &str) // 检验自我赋值return *this;delete[] m_date;m_date = new char [strlen(str.m_date)+ 1];strcpy(m_date, str.m_date);return *this;
} inline
ostream & operator << (ostream & os, const String & str)
{os << str.get_c_str();return os;int main(){return 0;
}
类与类之间的关系
复合(Composition)
例子: 表示has-a
一个类复合另一个功能更强大的类只开放这个类的部分功能(开放的这个类与调用的类同时加载)
Untitled
构造由内向外构造

在掉用内部的构造函数时编译器只会调用默认构造函数,如果想要调用其他构造函数想要在外层的构造函数里面调用
析构由外向内析构


代码实现
tempale <class T>
class quene{
...
protected:deque<T> c;
public:bool empty() const { return c.empty(); }size_type size() const { return c.size(); }reference front() { return c.front(); }referrece back() { return c.back(); }void push (const value_type& x) { c.push_back(x); }void pop() { c.pop_front(); }
}
委托(Delegation)
composition by reference
例子:
编译防火墙,客户端与实现类分离(reference counting)

用指针调用其他类(只有在调用的时候才会构造这个类,用指针相连寿命不一致)

代码实现
class StringREp;
class String {
public:String();String(const char *s);String(const String& s);String &operator=(const String& S);~String ();
pricvate:StringRep *rep;
}
继承(Inheritance)
继承语法:(is a关系)

父类的数据是可以完美继承下来的
子类的对象会有父类的成分

调用构造函数与析构函数的情况与复合关系时相似,调用的是默认构造函数


代码实现
struct _List_ndoe_base
{_List_node_base* _M_next;_List_node_base* _M_prev;
};tmplate<typename _Tp>
stuct _List_node:public _list_ndoe_base // 三种继承方式public是一种
{_Tp _M_date;
}
类中三种访问权限
public 类内可以用,类外可以访问
protected 类内可以访问 类外不可以访问
private 类内可以访问 类外不可以访问
两者主要在继承的时候有区别
在派生类中protected的数据可以访问,而private不能访问,通过类创建出来的对象也无法访问
struct 与 class 区别
最本质的一个区别就是默认的访问控制:默认的继承访问权限
struct是public的,class是private的。
struct A
{char a;
};
struct B : A
{char b;
}
这个时候B是public继承A的。
如果都将上面的struct改成class,那么B是private继承A的。这就是默认的继承访问权限
public继承还是private继承,取决于子类而不是基类。
struct可以继承class,同样class也可以继承struct,默认的继承访问权限是看子类到底是用的struct还是class
struct A{};
class B : A{}; //private继承
struct C : B{}; //public继承
要做的更像是一种数据结构的话,那么用struct。要做的更像是一种对象的话,那么用class
struct可以像c一样用大括号直接赋值,如A a={‘p’, 7, 3.1415926}; 但是在struct内部加入一个函数又不能赋值了,class把访问属性改为public也能赋值
虚函数
例子:
一个形状类(类似于框架,部分属性待定,与递归相似)

纯虚函数:父类不定义(大部分),留给子类定义(子类必须定义)
虚函数:父类有定义,子类可以重新定义
非虚汗:子类不能定义
继承一般要配合虚函数使用
template mathod

面向对象进阶
转换函数(conversion function)
当必要时,c++可以把创建的类自动转换为一个值
class Fraction
{
public:Fraction(int num, int den=1): m_numerator(num), m_denominator(den) { } operator double() const { // 这里的意思是当需要分数化为double型的时候,编译器可以直接转return (double)(m_numerator / m_denomination); }
private:int m_numerator; //分子int m_denominator; // 分母}
}
调用时
Fraction f(3,5);
Fraction d2 = f+4;// f被自动转换
转换函数要注意不要与构造函数冲突,当冲突的时候可以加在构造函数前加上 explict ,表示只有构造的时候,构造函数才能使用,不要自动转换。
pointer-like classes 智能指针
实例:迭代器
template<class T>
class shared_ptr
{
public:T& operator*() cosnt // 注意c++对于这两种操作符的重载{ return *px; }T& operator->() cosnt{ return px; }shared_ptr(T* p) : px(p){ }
private:T* px;
}
function-like classes 仿函数
重载小括号
struct identity
{const T&operator() (const T& x) const { return x; } // 括号里的就是参数
};
模板
类模板
调用的时候要用<>指名类型
函数模板
直接调用
成员模板
在模板类里面,又有模板
用于实现下面这种关系

模板全特化
//泛华代码
template<class Key>
struct hash { };
//------------------------------------
//特化
template<>
struct hash<int>{sise_t operator()(int x) const { return x; }};
类模板偏特化
类型偏特化:
当我们需要指定部分的模板变量的时候,需要偏特化
#include
#include
using namespace std;template<typename T, typename T1>
class A {public:A() = default;A(const T1& n) {cout << n << endl;}bool cmp(const T &t1, const T &t2) {return t1 == t2;}
};template<typename T1> // 篇特化
class A<char*, T1> {public:A() = default;A(T1& n) {cout << n << endl;}bool cmp(const char* t1, const char* t2) {while(*t1 != '\0' && *t2 != '\0') {if(*t1 != *t2) {return false;}++t1;++t2;}return true;}
};int main() {char* p1 = "hello";char* p2 = "hello";A<int, char*>c(p1);cout << c.cmp(1, 2) << endl;A<char*, char*>c1(p2); // 即使是偏特化,也要全部声明模板cout << c1.cmp(p1, p2) << endl;return 0;
}
范围偏特化:
template <typename T>
class C {...}; //此泛化版本的T可以是任何类型template <typename T>
class C<T*> {...}; //特化版本,T为指针类型 将范围缩小
函数在全特化的时候只需要在函数前面加上template<>就行。并且函数又函数重载的概念所以没有偏特化。
模板的模板参数:
当需要一个变化的容器,没有确定传入这个容器的变量类型时用模板的模板
template<typename T,template <typename T>class SmartPtr>
class XCLs
{
private:SmartPtr<T> sp;
public:XCLs():sp(new T){ }
};// 调用方式
XCLS<strin, shared_ptr> p1;
注意:这里待定的容器不能有默认参数,当有默认参数再使用模板,会报错,可以用c++2.0来解决
模板参数可变化
将传入的参数分为一个和一包
void print(){
}template<typename T,typename... Types>
void print(cosnt T& firstArg,cosnt Type&... args)
{cout << first << endl;print(args...);
}// sizeof...(args)
auto 语法糖
auto会自己推出类型,必须要在定义的时候就赋值
for循环
for( decl : coll ){ //coll是容器
statement;
}
for (auto i : {1,2,3,4,5,6,7}){ // i为后面的容器的值pass by valuecout << i << endl; // 如果要改后面的值 则需要auto&传引用
}
对象模型 虚指针 续表

带着虚函数的对象会多一个指针(虚指针),指向虚表。
后面继承的对象可以重写虚函数,改掉虚表的地址位置不变。
动态绑定条件:
1.通过指针调用
2.有向上转型的动作
3.调用虚函数
this
每个成员函数都有的一个this的指针(指向这对象的地址)
执行动态绑定

静态绑定与动态绑定
在vs中实际的测试带码和结果如下
#include
using namespace std;
class father
{
public:virtual void fun1(){cout << "father" << endl;}
};class child:public father
{
public:virtual void fun1() {cout << "child" << endl;}
};int main() {child c;father f;
// 静态绑定father f_n = (father)c;f_n.fun1();
// 动态绑定father* p_f_n = (father *)&c;p_f_n->fun1();
// 动态绑定father* p_f_n_n = new father;p_f_n_n->fun1();
// 动态绑定child* p_c_n_n = new child;p_c_n_n->fun1();
// 动态绑定father& d_c = c;d_c.fun1();return 0;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
