网站改版不换域名怎么做,四川网站建设免费咨询,群晖搭建wordpress不加端口,公司网站建设知乎1 类型转换构造函数
1.1 why? 基本类型之间的转换#xff0c;编译器内置转换规则#xff1a;int - double 类类型之间的转换#xff0c;编译器不知道转换规则#xff0c;需要用户提供#xff1a;Cat - Dog
// consconv_why.cpp 为什么需要自定义转换
#includ…
1 类型转换构造函数
1.1 why? 基本类型之间的转换编译器内置转换规则int - double 类类型之间的转换编译器不知道转换规则需要用户提供Cat - Dog
// consconv_why.cpp 为什么需要自定义转换
#include iostream
using namespace std;class Cat {
public:Cat( const char* name ) : m_name(name) {//【string m_name(name);】}void talk( ) {cout m_name : 喵喵~~~ endl;}
private:string m_name;
};class Dog {
public:Dog( const char* name ) : m_name(name) {//【string m_name(name);】}void talk( ) {cout m_name : 汪汪~~~ endl;}
private:string m_name;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {Cat smallwhite(小白);smallwhite.talk( );Dog bigyellow smallwhite; // Cat -- Dogreturn 0;
}
1.2 理论 定义1单参构造 (同于拷贝构造函数) 2参数类型与类类型不同 (异于拷贝构造函数) (单参构造就2种参数类型同于类类型就是拷贝构造否则就是类型转换构造) class 目标类型 { 目标类型 ( const 源类型 src ) { ... } // 类型转换构造函数 }; 用于 1利用一个已定义的对象来定义另一个不同类型的对象 2实现从源类型到目标类型的隐式类型转换 通过explicit关键字可以强制 这种通过类型转换构造函数实现的类型转换 必须通过静态转换显示地进行 class 目标类型 { explicit 目标类型 ( const 源类型 src ) { ... }; };
// consconv.cpp
// 类型转换构造函数 -- 指定 源类型 到 目标类型 的 转换规则
#include iostream
using namespace std;class Cat {
public:explicit Cat( const char* name ) : m_name(name) { // 类型转换构造函数//【string m_name(name);】cout Cat类的类型转换构造函数被调用 endl;}void talk( ) {cout m_name : 喵喵~~~ endl;}
private:string m_name;friend class Dog; // 友元声明
};class Dog {
public:Dog( const char* name ) : m_name(name) { // 类型转换构造函数//【string m_name(name);】}explicit Dog( const Cat c ) : m_name(c.m_name) { // 类型转换构造函数(Cat--Dog的转换规则)//【string m_namec.m_name;】cout Dog类的类型转换构造函数被调用 endl;}void talk( ) {cout m_name : 汪汪~~~ endl;}
private:string m_name;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
// Cat smallwhite(小白); // 定义smallwhite,利用smallwhite.Cat(小白)-类型转换构造函数// Cat smallwhite 小白; // 定义 匿名Cat类对象,利用 匿名Cat类对象.Cat(小白)-隐式转换// Cat smallwhite匿名Cat类对象--克隆Cat smallwhite static_castCat(小白); // 定义 匿名Cat类对象,利用 匿名Cat类对象.Cat(小白)-静态转换// Cat smallwhite匿名Cat类对象--克隆smallwhite.talk( );// Dog bigyellow(smallwhite); // 定义bigyellow,利用bigyellow.Dog(smallwhite)-类型转换构造函数// Dog bigyellow smallwhite; // 定义 匿名Dog类对象,利用 匿名Dog类对象.Dog(smallwhite)-隐式类型转换// Dog bigyellow 匿名Dog类对象--克隆Dog bigyellow static_castDog(smallwhite); // 定义 匿名Dog类对象,利用 匿名Dog类对象.Dog(smallwhite)-静态类型转换// Dog bigyellow 匿名Dog类对象--克隆bigyellow.talk( );return 0;
} 2 析构函数
2.1 理论 析构函数的函数名就是在类名前面加“~”没有返回类型也没有参数不能重载。 在销毁对象之前一刻自动被调用且仅被调用一次 - 对象离开作用域 栈对象离开main函数 - delete操作符 堆对象被释放 作用销毁 对象的各个成员变量 如果一个类没有定义析构函数那么编译器会为其提供一个默认的析构函数 - 对基本类型的成员变量什么也不做。 - 对类类型的成员变量调用相应类型的析构函数。 - 销毁 对象的各个成员变量
2.2 析构过程 对象的销毁过程 1调用析构函数陷 - 执行自己在析构函数中书写的代码 - 利用类成员变量调用相应的析构函数 - 释放对象的各成员变量所占内存空间 2释放整个对象所占用的内存空间 皮
// 析构函数
#include iostream
using namespace std;class Human {
public:
// 如果类没有提供任何构造函数,编译器将提供一个无参的构造函数
/* Human() {【int m_age;】定义m_age,初值为随机数【string m_name;】定义m_name,利用m_name.string()}*/Human( int age0, const char* name无名 ) : m_age(age),m_name(name) {//【int m_ageage;】定义m_age,初值为age//【string m_name(name);】定义m_name,利用m_name.string(name)cout Human类缺省构造函数被调用 endl;}
// 如果类没有提供拷贝构造函数,编译器将提供一个默认的拷贝构造函数
/* Human( const Human that ) { 【int m_agethat.m_age;】定义m_age,初值为that.m_age【string m_name(that.m_name);】定义m_name,利用m_name.string(that.m_name)--string类拷贝构造函数}*/Human( const Human that ) : m_age(that.m_age), m_name(that.m_name) { //【int m_agethat.m_age;】定义m_age,初值为that.m_age//【string m_name(that.m_name);】定义m_name,利用m_name.string(that.m_name)cout Human类拷贝构造函数被调用 endl;}
// 如果类没有提供拷贝赋值函数,编译器将提供一个默认的拷贝赋值函数
/* Human operator( const Human that ) {this-m_age that.m_age;this-m_name that.m_name; // this-m_name.operator(that.m_name)--string类的拷贝赋值函数return *this;}*/Human operator( const Human that ) {// 编译器不会再拷贝赋值函数中塞任何操作cout Human类的拷贝赋值函数被调用 endl;this-m_age that.m_age;this-m_name that.m_name; // this-m_name.operator(that.m_name)--string类的拷贝赋值函数return *this;}
// 如果类没有提供析构函数,编译器将提供一个默认的析构函数
/* ~Human() {对于基本类型成员变量m_age,什么都不做对于类类型成员变量m_name,利用 m_name.~string()释放 m_age/m_name 本身所占内存空间}*/~Human() {cout Human类的析构函数被调用 endl;// 对于基本类型成员变量m_age,什么都不做// 对于类类型成员变量m_name,利用 m_name.~string()// 释放 m_age/m_name 本身所占内存空间}void getinfo( ) {cout 姓名: m_name , 年龄: m_age endl;}
private:int m_age; // 基本类型的成员变量string m_name; // 类类型的成员变量
};// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {Human h; // 定义h,利用h.Human()--h维护的内容为(无名,0)h.getinfo( );Human h2(22,张飞); // 定义h2,利用h2.Human(22,张飞)--h2维护的内容为(张飞,22)h2.getinfo();Human h3(h2); // h2; // 定义h3,利用h3.Human(h2)--触发拷贝构造函数h3.getinfo();Human h4; // 定义h4,利用h4.Human()--h4维护的内容为(无名,0)cout h4被赋值前---;h4.getinfo();h4 h3; // h4.operator(h3)--触发拷贝赋值函数cout h4被赋值后---;h4.getinfo();cout ------------main will be over---------------- endl;return 0;
} //(1) h.~Human() h2.~Human() h3.~Human() h4.~Human() (2)释放h/h2/h3/h4本身所占的内存空间//(1) 删馅 (2)删皮
2.3 has to 通常情况下若对象在其声明周期的最终时刻并不持有任何动态分配的资源可以不定义析构函数。 若对象在其声明周期的最终时刻持有动态资源则必须定义析构函数释放对象所持有的动态资源。
// hastodes必须自己写析构函数的情况 -- 对象临死时持有动态资源
#include iostream
using namespace std;class A {
public:A(int i) : m_i(i), m_p(new int), m_f(open(./file, O_CREAT|O_RDWR,0644)) {//【int m_ii;】定义m_i,初值为i//【int* m_pnew int;】定义m_p,初值为指向一块堆内存(动态资源)//【int m_fopen(...);】定义m_f,初值为文件描述符--文件表等内核结构(动态资源)}~A() {delete m_p;close( m_f ); // 动态资源需要自己写代码释放// 释放m_i / m_p / m_f 本身所占内存空间}/* 默认析构函数~A() {释放m_i / m_p / m_f 本身所占内存空间}*/
private:int m_i;int* m_p;int m_f;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {A a; // 定义a,利用a.A()return 0;
} // a.~A() 释放a本身所占内存空间 析构函数的功能并不局限在释放资源上它还可以执行 我们希望在 对象被释放之前 执行的任何操作。 3 深拷贝
3.1 浅拷贝缺陷 如果类不提供拷贝构造编译器将提供默认的拷贝构造。 无论是 拷贝构造 还是 拷贝赋值其默认实现对任何类型的指针成员都是简单地复制地址而并不复制地址指向的数据这种情况称为浅拷贝。左图 为了获得完整意义上的对象副本必须自己定义 拷贝构造 和 拷贝赋值针对指针型成员变量做深拷贝。右图 // copybytes_pre.cpp 类中有指针成员默认拷贝构造 会有浅拷贝缺陷
#include iostream
#include cstring
using namespace std;
// 模拟C标准的string类 实现自己的String类
class String {
public:String( const char* psz ) : m_psz(new char[strlen(psz)1]) {//【char* m_psznew char[strlen(psz)1];】// 动态资源strcpy( m_psz, psz );}~String( /* String* this */ ) {delete[] this-m_psz;// 释放 m_psz 本身所占内存空间}char* c_str() { return m_psz; }
// 默认的拷贝构造
/* String( const String that ) {【char* m_psz that.m_psz;】只复制了地址没有复制地址指向的数据--浅拷贝}*/
private:char* m_psz;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {String s1(hello);cout s1: s1.c_str() , s1中的m_psz指向的堆内存的地址: (void*)s1.c_str() endl;String s2(s1); // s1; 定义s2,利用s2.String(s1)--拷贝构造函数cout s2: s2.c_str() , s2中的m_psz指向的堆内存的地址: (void*)s2.c_str() endl;return 0;
} // s1.~String() s2.~String() 相对于拷贝构造拷贝赋值需要做更多的工作 - 避免自赋值 - 分配新资源 - 拷贝新内容 - 释放旧资源 - 返回自引用
// copybytes.cpp 类中有指针成员默认拷贝构造 会有浅拷贝缺陷
#include iostream
#include cstring
using namespace std;
// 模拟C标准的string类 实现自己的String类
class String {
public:String( const char* psz ) : m_psz(new char[strlen(psz)1]) {//【char* m_psznew char[strlen(psz)1];】// 动态资源strcpy( m_psz, psz );}~String( /* String* this */ ) {delete[] this-m_psz;// 释放 m_psz 本身所占内存空间}char* c_str() { return m_psz; }
// 默认的拷贝构造
/* String( const String that ) {【char* m_psz that.m_psz;】只复制了地址没有复制地址指向的数据--浅拷贝}*/// 深拷贝构造函数String( const String that ) : m_psz(new char[strlen(that.m_psz)1]) {//【char* m_psz new char[strlen(that.m_psz)1];】strcpy( m_psz, that.m_psz ); // 不复制地址复制地址指向的数据--深拷贝}/* 默认拷贝赋值函数String operator( const String that ) {this-m_psz that.m_psz; // 只复制了地址没有复制地址指向的数据--浅拷贝return *this;}*/// 深拷贝赋值函数String operator( const String that ) {if( this ! that ) { // 防止自赋值delete[] this-m_psz; // 释放旧资源this-m_psz new char[strlen(that.m_psz)1]; // 分配新资源strcpy( this-m_psz, that.m_psz ); // 拷贝新内容}return *this; // 返回自引用}
private:char* m_psz;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {String s1(hello);cout s1: s1.c_str() , s1中的m_psz指向的堆内存的地址: (void*)s1.c_str() endl;String s2(s1); // s1; 定义s2,利用s2.String(s1)--拷贝构造函数cout s2: s2.c_str() , s2中的m_psz指向的堆内存的地址: (void*)s2.c_str() endl;String s3; // 定义s3利用s3.String()--s3维护一个字节堆内存(\0)s3 s2; // s3.operator(s2)cout s3: s3.c_str() , s3中的m_psz指向的堆内存的地址: (void*)s3.c_str() endl;return 0;
} // s1.~String() s2.~String()
3.2 建议 1只有类中有指针型成员变量才会涉及深浅拷贝的问题因此应尽量避免使用指针型成员变量。 2如果确实无法实现完整意义上的 深拷贝拷贝构造 和 深拷贝拷贝赋值可将它们私有化禁止用户使用。 4 静态成员
4.1 静态成员变量 静态成员变量 不属于对象 而 属于类 - 静态成员变量不包含在对象中进程级生命期 - 静态成员变量的定义和初始化只能在类的外部(即全局域)而不能在构造函数中进行。 - 静态成员变量依然受 类作用域 和 访问控制限定符 的约束。 - 访问静态成员变量既可以通过 类 也可以通过 对象。 - 静态成员变量为该类的所有对象实例所共享。
// static.cpp 类的静态成员变量
#include iostream
using namespace std;// 普通成员变量:属于对象,对象的生命期 静态成员变量:不属于对象,进程级生命期
class A {
public:A() {//【int m_i;】}int m_i; // 声明static int m_si; // 声明
};
int A::m_si 0; // 全局域中定义--进程级生命期
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {A a, b; // 静态成员变量没有保存在对象内部--不属于对象cout a对象的大小: sizeof(a) endl; // 4cout b对象的大小: sizeof(b) endl; // 4A::m_si 888; // 静态成员受到类作用域的约束 也受到访问控制限定符的约束--属于类a.m_si 999; // A::m_si999;cout b.m_si b.m_si endl; // A::m_si// 类的静态成员变量被该类的所有对象共享return 0;
} 4.2 静态成员函数 静态成员函数 不属于对象(普通成员函数也不属于对象更准确地说不用非要对象来调) 而 属于类 - 静态成员函数没有this指针也没有常属性 - 静态成员函数依然受 类作用域 和 访问控制限定符的约束 - 访问静态成员函数既可以通过 类 也可以通过 对象。普通成员函数只对象 -静态成员函数只能访问静态成员而非静态成员函数可以访问所有成员。
// static.cpp 类的 静态成员变量 和 静态成员函数
#include iostream
using namespace std;// 普通成员函数必须利用对象来调用 静态成员函数不是必须利用对象来调用
class A {
public:int m_i; // 普通成员变量void foo( /* const A* this */ ) const { // 普通成员函数cout foo is invoked endl;cout m_i endl; // okcout m_si endl;// okbar(); // ok
// 以上三行代码证明 非静态成员函数 即可访问非静态成员 也可访问 静态成员(不挑食)}static int m_si; //静态成员变量static void bar( /*无this指针*/ ) /*const*/ { // 静态成员函数cout bar is invoked endl;cout m_si endl; // ok
// cout m_i endl; // error
// foo(); // error
// 以上三行代码证明 静态成员函数 只能访问 静态成员不能访问非静态的普通成员(挑食)}
};
int A::m_si 0; // 全局域中定义--进程级生命期
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {A a, b;a.foo(); // foo(a);b.foo(); // foo(b);A::bar(); // 受到类作用域的约束 也受到访问控制限定符的约束--属于类a.bar(); // A::bar();b.bar(); // A::bar();return 0;
} 4.3 总结 事实上类的静态成员变量和静态成员函数更像是普通的全局变量和全局函数 只是多了一层类作用域和访问控制限定符的约束 相当于 具有成员访问属性的全局变量和全局函数。 5 类 扩充 空类对象的大小是1个字节。 类中不能包含 本类对象 作为 普通成员变量 类中可以包含 本类对象 作为 静态成员变量。
// class_add
#include iostream
using namespace std;
/*
class A { // 空类
};
int main( void ) {A a; // 空类对象占1个字节内存(1个字节的垃圾数据)A ra a;cout 空类对象a的大小: sizeof(a) endl;return 0;
}
*/class A {
public:int m_i;
// A m_a; // errorstatic A m_sa; // ok
};
int main( void ) {A a; // 定义a(给a分配内存空间)cout 对象a的大小: sizeof(a) endl;return 0;
} 6 单例模式 要求设计一个类要求用户在使用这个类时仅有一个实例只能出现一个对象: class Singleton { // 设计这个类 }; int main(void) { // 用户这里只能出现一个Singleton类对象不能出现第二个 return 0; } 实现方法 1将 包括类的拷贝构造函数在内的所有构造函数 私有化防止user在类的外部创建对象。 2唯一的对象由类的设计者来创建 3提供公有静态成员函数getInstance()使用户可以获取到唯一对象。 单例分类 1饿汉式无论用不用程序启动即创建 hungry.cpp 不推荐 2懒汉式用的时候创建不用了即销毁 lazy.cpp 推荐
// hungry_singleton.cpp
// 单例模式--要求设计一个类型用户在使用这个类时只能出现一个对象
#include iostream
using namespace std;
// 恶汉式单例
class Singleton {
public://4 //5static Singleton getInstance( ) {return s_instance;}
private:Singleton( ) {} // 1Singleton( const Singleton that ) {} // 6static Singleton s_instance; // 2 唯一对象
};Singleton Singleton::s_instance; // 3// 以上代码模拟类的设计者
// -----------------------
// 以下代码模拟类的使用者
int main( void ) {Singleton s1 Singleton::getInstance( );Singleton s2 Singleton::getInstance( );Singleton s3 Singleton::getInstance( );cout s1: s1 , s2: s2 , s3: s3 endl;return 0;
} // lazy_singleton.cpp
// 单例模式:设计一个类保证用户在使用这个类时只能出现一个对象
#include iostream
using namespace std;
// 懒汉式单例:单例高级实现手法
class Singleton {
public:static Singleton getInstance( ) {if( s_instanceNULL ) {s_instance new Singleton; // 唯一的对象cout 创建了唯一的对象 endl;}s_counter;return *s_instance;}void releaseInstance( ) {if( --s_counter 0 ) {delete s_instance;s_instance NULL;cout 销毁了唯一的对象 endl;}}
private:Singleton() {} Singleton( const Singleton that ) {} static Singleton* s_instance; // 并不是唯一对象,仅仅是一个指针而已static int s_counter; // 计数功能
};Singleton* Singleton::s_instance NULL; // 程序刚刚时唯一的对象不存在
int Singleton::s_counter 0;// 以上的代码模拟类的设计者(例如:类库、被人设计的类、自己的设计的类)
// -------------------------------------
// 以下的代码模拟用户(使用类的人)
int main( void ) {Singleton s1 Singleton::getInstance(); // 第一次调用getInstance函数时,创建唯一的对象Singleton s2 Singleton::getInstance(); // 以后再调用getInstance函数时,返回第一次调用时创建的对象Singleton s3 Singleton::getInstance(); // ...cout s1: s1 , s2: s2 , s3: s3 endl;s1.releaseInstance( ); //s2.releaseInstance( ); // s3.releaseInstance( ); // 最后一次调用releaseInstance才将对象销毁return 0;
}