C++学习之六

std::unique_ptr func(void)
{
unique_ptr pr(new string(“i love china”));
return pr; //返回一个局部对象
}
int main(void)
{
unique_ptr ps = func();
return 0;
}

2)unique_ptr删除器
void deleteFunc(string* pdel)
{
delete pdel;
pdel = nullptr;
cout << “8888888” << endl;
}
int main(void)
{
using ptr = void ()(string); //using定义函数指针
//typedef void (ptr)(string);
unique_ptr<string, ptr> p1(new string(“abc”), deleteFunc);
return 0;
}

成员函数可以调用成员函数,我不管你是共有的还是私有的,都可以;
class A
{
public:
void func(void)
{
cout << “00000000” << endl;
func2(); //成员函数可以调用成员函数,我不管你是共有的还是私有的,都可以;
}
private:
void func2(void)
{
cout << “0***********” << endl;
}
};
int main(void)
{
A* a = new A();
a->func();
return 0;
}

//生成对象的时候,可以有多种方式;
class A
{
public:
A(int a,float b):a_(a),b_(b)
{
cout << “构造函数” << endl;
}
private:
int a_;
float b_;
};
int main(void)
{
A a{ 3,4.6 };
A a2(4, 5.7);
A a3 = A(3, 6);
A a4 = A{ 5,3.7 };
A a2 = { 5,5.0 }; //等号、大括号进行的是隐式类型转换
//调用的都是拷贝构造函数
A a11{ a };
A a21(a);
A a31 = A(a);
A a41 = A{ a };
A a41 = { a };
return 0;
}

//用一个对象,初始化另一个对象,就叫拷贝;
函数默认参数:默认值只能放在函数声明中,除非该函数没有函数声明;
一旦出现默认值,则右边的参数必须都得指定默认值;

//隐式转换和explict
class A
{
public:
A(int a)
{
b_ = 45.9;
a_ = a;
cout << “构造函数2” << endl;
}
private:
int a_;
float b_;
};

void func(A a)
{

}
int main(void)
{
//A a1 = 3; //出现了隐式类型转换,45被转换了一个临时对象
//A a2 = (4, 2, 1, 7, 8);//出现了隐式类型转换,最后参数是8被转换为临时对象

func(45);//45被转换了一个临时对象,调用了构造函数
return 0;

}

A aa = {54}; //大括号里面可以认为是一个对象;
对应单参数的构造函数,一般都声明为explicit;
必须用构造函数初始化列表的情况;

初始化和赋值是完全两个概念,所以最好用构造函数初始化列表(效率更高);
在构造函数初始化列表中,不要用一个成员变量给另一个成员变量赋值;
构造函数被当成inline定义,多个文件包含的成员函数,不会重复定义;
成员函数末尾的const; 作用:告诉系统,这个成员函数不会修改该对象的任何成员变量;
带const的成员函数也叫常量成员函数;声明中加const,定义中也加const;
const Time abc; //定义const对象;

1)//常对象及常量成员函数
class A
{
public:
void func(void)const
{
cout << “123” << endl;
}
void func1(void)
{
cout << “456” << endl;
}
};

int main(void)
{
const A a1;
a1.func(); //只能调用带const的成员函数,因为是常对象;
A a2;
a2.func1(); //可以调用带const的成员函数,也可以调用不带const的成员函数;
return 0;
}

2)multable,与const相反,为了突破const的限制;
class A
{
public:
void func(void)const //怎么样可以让a_赋值有效呢,去除const的属性的方法:mutable
{
a_ = 5;
cout << “123” << endl;
}
A(int a) :a_(a)
{
cout << a_ << endl;
}
private:
mutable int a_; //用mutable修饰成员变量,这样就可以在const的成员函数中修改其变量内容;
};

int main(void)
{
A a(4);
a.func();
return 0;
}

//this就是对象本身!!!
this是带有(const)常量属性的;//伪代码是: Time
const this;
//this指针中,添加const,代表只能指向当前对象,不能指向其他对象了;
class A
{
public:
A& func1(void)
{
cout << “123” << endl;
return *this;
}
A& func2(void)
{
cout << “123----” << endl;
return *this;
}
};

int main(void)
{
A a1;
a1.func1().func2(); //实现连调用机制
return 0;
}

//静态变量的使用方法
class A
{
public:
static int m_; //声明静态成员变量
static void show(void)
{
cout << “444” << endl;
}
};

int A::m_ = 20; //定义的时候不要再加static,声明的时候加static

int main(void)
{
cout << A::m_ << endl; //下面三句打印指向的是同一块内存;
A a1;
cout << a1.m_ << endl;
A a2;
a2.m_ = 90;
cout << a2.m_ << endl;
return 0;
}

在调试模式下:shift+F9,快速打开监视模式

//类相关的非成员函数
class A
{
public:
int a_;
int b_;
};

void printMsg(const A& obj)
{
cout << obj.a_ << " " << obj.b_ << endl;
}
int main(void)
{
A a1;
a1.a_ = 90;
a1.b_ = 40;
printMsg(a1);
return 0;
}

//类内初始化
class A
{
public:
int a_ = 6; //类内初始化
int b_ = 54;
};

void printMsg(const A& obj) //非成员函数调用类
{
cout << obj.a_ << " " << obj.b_ << endl;
}
int main(void)
{
A a1;
printMsg(a1);
return 0;
}

5)const成员变量的初始化:必须用构造函数初始化列表进行初始化;
不可以通过赋值进行初始化;
class A
{
public:
const int m;
A(int a) :m(a) //const成员变量必须是初始化列表进行初始化
{
cout << "constructor— m: " <<m<< endl;
}
};

int main(void)
{
A a(56);
return 0;
}

//没有参数的构造函数,就叫默认构造函数!!!

6)=default,=delete的使用;C++11中引入的
=default : 让系统生成默认构造函数;
=delete; 禁止系统生成默认构造函数;

8月14日学习:
顶层const和底层const都是相对于指针而言的。const修饰的顺序是向左后右;
顶指(顶针)不变容可变(int* const p; ),底类(滴泪)不变指可变 (const int* p); 有个成语叫(顶针滴泪)

虚函数调用
People* p2 = new Man(); //父类指针new一个子类对象
p2->eat();
p2->People::eat(); //显示调用父类的成员函数

//p2 = new People();
//p2->eat();

//p2 = new Women();
//p2->eat();
//override 只能写在子类中,override只能用在虚函数中;
final也是虚函数专用,使用在父类中,尝试覆盖父类的代码是会出错的;
int main(void)
{
Man m1; //输出以下结果正常
//people have argument constructor----
//man constructor----
//people have argument constructor----
//man constructor----
Man* m2 = new Man(); 需要自己手动delete m2;否则会造成内存泄漏,如果手动delete,也没毛病

People* p1 = new Man(); /父类指针指向子类对象
delete p1; //因为父类虚构函数没有设置为虚析构函数,没有执行子类的析构函数,造成内存泄漏;
//输出结果如下,	
//people default constructor---- 
//man constructor----
//~people  deconstructor----

return 0;

}

2)友元函数和友元类
class Man : public People
{
public:
friend void func(const Man& obj); //设置func为本类的友元函数,就可以反问本类的私有成员函数;
Man();
~Man()
{
cout << “~Man deconstructor----” << endl;
}
private:
virtual void eat(void)const override
{
cout << “男人吃米饭” << endl;
}
};
//只要让函数func成为类Man的友元函数,那么func函数就能够访问类Man的所有成员;(包括成员变量和成员函数)
void func(const Man& obj) //如果不设置为func函数为Man类的友元函数,下面代码会编译错误;
{
obj.eat();
}

有缘关系不能被继承,是单向性的,没有传递性的;
友元成员函数的学习;

3)友元类的最简单写法,代码如下:
class A
{
friend class B; //声明B为自己的友元类,这样才可以反问自己的私有成员mm;
private:
int mm = 6;
};

class B
{
public:
void printMsg(const A& obj)
{
cout << obj.mm << endl;
}
};
int main(void)
{
A a;
B b;
b.printMsg(a);
return 0;
}

4)友元成员函数案例:
class B;
class A
{
public:
void printMsg(const B& obj);
};

class B
{
friend void A::printMsg(const B& obj); //友元成员函数的书写格式需要注意
private:
int mm = 6;
};

void A::printMsg(const B& obj)
{
cout << obj.mm << endl;
}
int main(void)
{
A a;
B b;
a.printMsg(b);
return 0;
}
重点: A类 和B类的顺序不能改变。因为:只有一个类的定义被看到时,它的成员函数才能被声明为另一个类的友元。
因为这种声明和定义的依赖关系在里边;
注意友元成员函数的声明书写格式;
友元的好处:允许在特定情况下,某些非成员函数可以访问类的protected、private成员;(public本身就可以访问)

5)typeid运算符,返回指针或者引用所指对象的实际类型;

int a = 20;
const int* const p = &a; //格式的正确写法, ‘p’ 只不过是一个名字而已,前面的 '’ 才是代表的指针类型;
Base
pb2 = dynamic_cast <Base*> (new Derived); //正确写法,必须记住

重点:要想让RTTI的两个运算符(typeid和dynamic_cast)正常工作,基类必须有虚函数,要不然两个运算符工作的结果可能不对;
因为只有虚函数的存在,两个运算符才可以起到动态类型的作用;、
为者常成,行者常至;
class Base
{
public:
Base(void) { cout << “Base constructor” << endl; }
virtual void func2(void) { cout << “func2 handle—” << endl; }
};

class Derived : public Base
{
public:
Derived(void) { cout << “Derived constructor” << endl; }
void func(void) { cout << “func handle—” << endl; }
};

class Test : public Base
{

};
int main(void) //父类指针指向子类对象,但没办法调用子类对象的成员函数;
{
Base* bptr = new Derived();
Test* dptr = dynamic_cast<Test*>(bptr); //dynamic_cast只用作于指针和引用的转换;
//基类对象的指针或引用转换为【同一继承层次】的其他指针或引用;
if (dptr != nullptr) //如果转成功,那就不为空;测试如果是转Test则不成功,转Derived則成功;
{
cout << “转换成功” << endl;
//dptr->func();
}
else
{
cout << “没有转换成功” << endl;
}
return 0;
}
//dynamic_cast主要用于【安全的向下转型】,【向上转型】本身就安全,不必用dynamic_cast;
//静态类型时,向上转型也安全,但是向下类型转换不允许!派生类向基类的隐式类型转换,是可以的;反过来不行。
//引用转换失败,则系统会抛出异常了 std::bad_cast; try() catch{}
测试代码如下:
class Base
{
public:
Base() {};
virtual void Show() { cout << “This is Base calss”; }
};
class Derived :public Base
{
public:
Derived() {};
void Show() { cout << “This is Derived class”; }
};

int main()
{
//第一种情况,转换成功
Derived b;
Base& base1 = b;
Derived& der1 = dynamic_cast<Derived&>(base1);
cout << “第一种情况:”;
der1.Show();
cout << endl;

//第二种情况
Base a;
Base& base = a;
cout << "第二种情况:";
try {
    Derived& der = dynamic_cast<Derived&>(base);
}
catch (bad_cast)
{
    cout << "转化失败,抛出bad_cast异常" << endl;
}

}

6)typeid(指针或者引用) //typeid是运算符,返回的是一个常量对象的引用;
typeid的作用:typeid主要是为了比较两个指针是否指向同一种类型的对象;
在虚函数表里面,第一项就是type_info对象;

7)final用在类的后面,如下所示:
class Base final {};//代表此类不能再继承了;
静态类型:变量声明时候的类型,静态类型是已知的;
动态类型:指的是指针或者引用所代表内存中对象的类型;
只有基类指针或者引用,才存在静态类型和动态类型不一致的情况;

父类与子类的拷贝与赋值,代码如下:
class Base
{
public:
Base() {};
virtual void Show() { cout << “This is Base calss”; }
Base(const Base& obj)
{
cout << “base copy constructor” << endl;
}
};
class Derived : public Base
{
public:
Derived() {};
void Show() { cout << “This is Derived class”; }
};

int main()
{
Derived d1;
Base b1(d1); //子类对象初始化父类对象,其实引用的还是父类的成员
//基类只能处理自己的成员,无法处理派生类的成员;
//或者如下,调用拷贝赋值运算符:
Derived d1;
Base b1;
b1 = d1;
return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30143193/article/details/132345320
今日推荐