C++Primer学习——类
我们在创建类的对象时,类不应该仅仅被声明,还应该被定义过,否则无法知道类占用了多少的内存
但是如果一个类的名字已经出现过就被认为是已经声明过了,所以允许包含自己的指针或者引用。
默认构造函数:
当类中包含一个其他类的成员且它没有默认构造函数,那个编译器无法为当前类合成默认构造函数。
如果不支持内类初始值,那么所有构造函数都应显式的初始化每个内置类型成员
使用vector或string能避免分配、释放内存带来的复杂性
struct和class唯一的区别就是默认访问权限,struct(public) class(private)
初始化列表:
对象成员的初始化发生在进入构造函数本体之前,构造函数内的只能算是赋值
如果没有初始值列表显示初始化成员,会在构造函数之前执行默认初始化
(如果想创建一个常量对象,构造函数完成初始化过程,对象才真正有常量属性)。
class fo
{
public:
fo(int i);
private:
int ai;
const int ci;
int &ri;
}
fo::fo(int i)
{
ai = i;
ci = i; //error 不能给const赋值
ri = i; //error ri未初始化
}
fo::fo(int i):ai(i),ci(i),ri(i){} //correct
//如果成员是const,引用或者某需要提供默认初始值的类类型,要通过构造函数初始值列表提供。
如果构造函数没有初始值列表,那么我构造函数之前会先进行默认初始化
委托构造函数:
class sales
{
public:
sales(string s,int cnt):bookNo(s),units_sold(cnt){} //1
sales():sales("",0,0) {} //2 利用1
sales(string s):sales(s,0,0){} //3 利用1
sales(iostream &is):sales(){} //4 利用2
}
数据成员:
类内部的成员函数和友元函数是隐式内联的。
const成员函数(修改隐式this指针的类型):
所以一个成员函数被标记为const,则它不能调用一个非const的成员函数(隐式this指针,无法从const转变成非const)
class aa{
int num;
public:
void out2() const{
cout<<num<<endl;
}
void out3() const{
num+=10; //error,const函数不能修改其数据成员
cout<<num<<endl;
}
};
定义在类内部的函数是隐式inline.结构体内部this指向自己,一个常量指针.
但在const成员函数中,this会被修改成 指向常量的常量指针,所以无法修改值
编译器会首先编译成员声明,然后才是成员函数
友元
有的函数是类接口的一部分,但又不是类的成员。友元可以允许其它类或者函数访问它的非公有成员。
友元只能声明在类内部,不是类的成员所以不受它所在区域的访问控制级别的约束。
不具有传递性、不能被继承、单向无交换性
令成员函数作为友元,注意作用域:
先定义man,声明disp不定义。在disp使用woman前声明它。
定义woman然后disp的友元声明。
定义disp
//成员函数的friend声明必需在定义之前,需要用到类限定符。所以man必须先被定义
class woman; // 前向声明
class man
{
public:
void disp(woman &w);
};
class woman
{
public:
friend void man::disp(woman &w); // 将man的其中一个成员函数disp()设为woman的友元函数,就可以使用该函数访问woman对象的私有成员了
private:
string name;
};
void man::disp(woman &w)
{
cout << w.name << endl;
}
类和非成员函数的声明不是必须在它们的友元声明之前。
struct X
{
friend void f() {]
X(){ f(); }
void g();
void h();
}
void X::g(){ return f(); }
void f();
void X::h(){ return f(); }
如果想把一组重载声明为友元,必需对戏中的每一个分别声明
可变数据成员:
有时候希望改变const对象里面的某个数据成员,可通过在数据成员加上mutable实现。
而一个可变的数据成永远不可能是const
mutable size_t acces;
返回*this的成员函数:
返回引用和非引用的区别
class Screen
{
public:
Screen &set(char);
Screen &move(int ,int);
};
inline Screen& Screen::set(char c){}
inline Screen& Screen::move(int x,int y){}
int main()
{
Screen myScreen;
myScreen.move(4,0).set('#');
myScreen.move(4,0); //如果返回引用,等效于此
myScreen.set('#');
Screen temp = myScreen.move(4,0); //如果返回非引用。。,则是一个副本
temp.set('#');
}
const成员返回*this时,不能嵌入一个动作序列
如果display返回常量引用,调用set则会产生错误。。无法修改一个常量对象
基于const的重载:
如果某个对象上面调用display,该对象是否是const决定了调用display的那个版本
Class A {
int display();
int display() const;
};
原因是:按照函数重载的定义,函数名相同而形参表有本质不同的函数称为重载。在类中,由于隐含的this形参的存在,const版本的 display函数使得作为形参的this指针的类型变为指向const对象的指针
不完全类型:
一旦一个类名出现,就被认为是声明过了(但尚未定义),因此允许包含自己的指针。
在声明之后,定义之前是一个不完全类型。
①可以定义指向这个的指针和引用 ②也可以声明以不完全类型作为参数或者返回类型的函数
class Link_screen
{
Screen windows;
Link_screen *next;
}
作用域:
一旦遇到类名,那么剩下的部分就在类的作用域之内了(参数列表和函数体)
class Window_mgr
{
public:
ScreenIndex addScreen(const Screen&);
};
Window_mgr::ScreenIndex
Window_mgr::addScreen(const Screen& a)
{}
对于类成员函数的名字查找:
1.编译成员的声明 2.直到类全部可见之后才编译函数体
class Account //balance会在整个类可见之后才被处理
{
public:
int balance(){return val;}
private:
int val;
}
类型名:
通常内层作用域可以重新定义作用域外的名字,即便这个已经被用过。
但是在类中,如果使用外层作用域的某个名字,而这名字代表一种类型,则不能重新定义
typedef double mon;
class Account{
public:
mon balance(){};
private:
typedef double mon; //不能重新定义mon
}
成员定义的普通块作用域的名字查找:
1.先成员函数内部 2.类内全面查找 3.成员函数定义之前的作用域
所以此例中生效的是pos height,但是它隐藏了同名的成员,对于被隐藏对象可以通过作用域符 ::
int height;
class Screen{
public:
typedef std::string::size_type pos;
void dup(pos height)
{
cursor = width*height; //width*this->height,不建议隐藏同名
}
private:
pos height = 0,width = 0;
pos curson;
}
类类型转换:
隐式转换:
能通过一个参数的拷贝构造函数定义一条从构造函数参数类型到类类型的隐式转换规则。
(只有一个实参的能用于隐式转换)
struct foo
{
foo(){}
foo(int a):real(a){}
// int b = 2;
const foo& operator+(foo c){
return foo(this->real + c.real);
}
int real;
};
int main()
{
foo f;
f = 10; //会生成一个临时的foo
cout <<f.real <<endl;
f = f + 100;
cout << f.real <<endl;
}
只允许一步转换:
string->foo correct
"9999"->string->foo error
item.combine("9999"); //error
item.combine(string("9999")); //correct 显式转换string,隐式转换
item.combine(sales("9999")); //correct 隐式转换string,显式转换sales
我们可以通过将构造函数声明为explicit阻止隐式转换,函数只能用于直接初始化
(只类内部的声明时使用,在类外定义时不需要重复explicit)
struct node
{
explicit node(){}
explicit node(string s){}
node& combine(node &b){}
};
int main()
{
string s = "xxx";
node a("xxx");
node b(s);
//node a = s; //error 没法隐式的创建对象node
//a.combine(s); //error
}
静态成员:
static将其与类绑定起来。在类外定义时不能重复static.一般来说不能在类内定义初始化静态成员(可能产生重复定义)
如果静态成员是constexpr类型并且初始值是常量表达式,可以在类内进行定义。
而且由于静态成员函数不与对象绑定,不包含this指针,所以也不能被声明为const
因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了
class Point
{
public:
void init()
{
}
static void output()
{
printf("%d\n", m_x); //error
}
private:
int m_x;
};
int main()
{
Point pt;
pt.output();
}
static数据成员,不可被定义在inline函数中.inline函数在编译时会被编译为2进制代码并重复嵌入各函数体中,而static类型数据成员只可被初始化一次,inline函数中使用static的话,将会造成static类型数据成员被多次初始化错误.
静态成员可以是不完全类型,静态成员可以作为默认实参
class Screen
{
public:
Screen &clear(char = back);
private:
static const char back;
};
C++Primer学习——类的更多相关文章
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
- 深入java虚拟机学习 -- 类的卸载
类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...
- Swift学习——类的定义,使用,继承,构造等(五)
Swift学习--类的定义,使用.继承,构造等(五) 类的使用说明 1 使用class和类名来创建一个类名,比如: class student 2 类中属性的声明和常量和变量一样,唯一的差别就是他们的 ...
- c++ primer 学习杂记2【派生类到基类转换的可访问性】
参考: http://blog.csdn.net/rehongchen/article/details/7930853 http://blog.csdn.net/ming_road/article/d ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...
- C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员
复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...
- C++ Primer 学习笔记_53_类和数据抽象 --友元、static员
分类 --友元.static成员 一.友元 友元机制同意一个类将对其.友元关系:一个样例 如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据.Screen应该同意其 ...
随机推荐
- NetFPGA-1G-CML Demo --- reference_router_nf1_cml
环境 deepin 15.4 vivado 15.2 ise 14.6 前期准备 Github Wiki链接:https://github.com/NetFPGA/NetFPGA-public/wik ...
- hashlib 加密
import hashlib def md5(args): hash = hashlib.md5(bytes('aaadf',encoding='utf-8')) hash.update(bytes( ...
- "未找到应用程序的“aps-environment”的权利字符串"
1.先生成App ID,在去Provisioning里面生成新的Profile 2.删除Xcode里面原来的push profile(如果没有就不用删除)再次双击新下载的profile(mobilep ...
- appcompat v21: 让 Android 5.0 前的设备支持 Material Design
1. 十大Material Design开源项目 2. appcompat v21: 让 Android 5.0 前的设备支持 Material Design 主题 AppCompat已经支持最新的调 ...
- zookeeper安装及环境变量设置
下载 首先去官网下载(自行选择版本):http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.11/然后执行tar -zxvf解压 对于后台安装 ...
- Spark性能优化总结
1. 避免重复加载RDD 比如一份从HDFS中加载的数据 val rdd1 = sc.textFile("hdfs://url:port/test.txt"),这个test.txt ...
- Nginx在windows环境下的安装与简单配置
版权声明:本文为博主原创文章,未经博主允许不得转载. 一. 下载并安装Nginx 去Nginx官网下载 我这里选取nginx/Windows-1.10.3版本,下载后解压出来即可,解压出来的路径不能含 ...
- LeetCode & Q121-Best Time to Buy and Sell Stock-Easy
Array DP Description: Say you have an array for which the ith element is the price of a given stock ...
- JAVA 中的 反射
CLASS类 1) 在面向对象的世界里,万事万物皆对象. 在java中有两样东西不是面向对象 1.普通的数据类型(java中有封装类来弥补它) 2. java中静态的东西 2) 类是对象吗? 类是对象 ...
- MySQL 主从复制那些事(一)
本部分主要以理论为主,具体的主从搭建环境,大家可以参考博客其他部分.下面我就给大家数说主从复制那些理论的东西.说的不一定都是正确的,有不同出入的地方,欢迎大家一起交流沟通,以下我把我自己整理出来的主从 ...