メンバ変数のconst

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
zxzy

メンバ変数のconst

#1

投稿記事 by zxzy » 12年前

コード:

class A{

	const int a;

}
のように値を変更しない変数をクラス内で宣言したいです。
この場合aの初期化は

コード:

A() : a(0) {}
のようにコンストラクタでしないといけないです。

私がしたいのはインスタンスごとに初期値が異なるもののため

コード:

A(int arg_a) : a(arg_a) {}
になります。

しかし、こうすると

コード:

A aa[2];
のように配列宣言すると引数ありコンストラクタを呼べないので
aを初期化できなくなります。

なんとかaに値を入れる方法がないでしょうか?

constをやめればいつでも代入でできるため値をいれれますが
変更しない値なのでconstのほうがいいように思います。

また、上記の例では必要なかったのでメンバ変数は1つですが
作るものは10を超える予定のため、コンストラクタの引数で

コード:

A aa(10,20,30,40,50,60,70,80,90,100);
のように書くよりは

コード:

a1 = 10;
a2 = 20;
:
:
のように見やすくひとつずつ代入のように値を入れていける方法が
いいです。


よい方法を教えて下さい。

zxzy

Re: メンバ変数のconst

#2

投稿記事 by zxzy » 12年前

追記です

できるだけ->より.を使いたいので

コード:

A* a[2];
a[0] = new A(10);
a[1] = new A(20);
のようなポインタは使わない方法でお願いします

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#3

投稿記事 by softya(ソフト屋) » 12年前

やりたい事は、こういう事でしょうか?

コード:

#include <iostream>
class A
{
	const int a;
	const int b;
public:
	A( int arg_a,int arg_b ) : a( arg_a ), b( arg_b ) {};
	void print() {
		std::cout << a << "," << b << std::endl;
	};
};

int main()
{
	A a[3] = {A(10,2), A(5,5), A(3,2)};
	for( int i = 0; i < 3 ; i++ ) {
		a[i].print();
	}
	return 0;
}
代入に関しては、C++の文法の範囲で良い方法が思いつきませんでした。
[追記]C#なら出来ると思うのですが、C++/CLIは分かりません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#4

投稿記事 by nullptr » 12年前

C++的に書くなら、まずデフォルトコンストラクタも書いときましょう。

コード:

class A{
    const int a;
public:
    A() : a( 0 ) {}    // デフォルトコンストラクタ。ダミーの初期化
    A(int arg_a) : a(arg_a) {}
}
まぁこれはいいとして、初期化、というかインスタンスの確保にplacement newを使いましょう。

コード:

#include <new> // *

int main(){
    A a[10];

   // ループ使う場合
    for( int i = 0; i < 10; ++i ){
        new( a + i ) A( 100 );
    }
    // 等等

    // 各々の場合
    new( a + 0 ) A( 10 );
    new( a + 1 ) A( 20 );
    new( a + 2 ) A( 30 );
    new( a + 3 ) A( 40 );
    new( a + 4 ) A( 50 );
    new( a + 5 ) A( 60 );
    new( a + 6 ) A( 70 );
    new( a + 7 ) A( 80 );
    new( a + 8 ) A( 90 );
    new( a + 9 ) A( 100 );
    new( a + 10 ) A( 110 );

};

んんー、placement newはヒープでは無く静的に配置されますから、動的な確保はいろいろアレですが、
定数をそれぞれ設定するって事はある程度数が予測できそうですからこんなんでいいんじゃないでしょうか
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: メンバ変数のconst

#5

投稿記事 by beatle » 12年前

zxzy さんが書きました:また、上記の例では必要なかったのでメンバ変数は1つですが
作るものは10を超える予定のため、コンストラクタの引数で

コード:

A aa(10,20,30,40,50,60,70,80,90,100);
のように書くよりは

コード:

a1 = 10;
a2 = 20;
:
:
のように見やすくひとつずつ代入のように値を入れていける方法が
いいです。
C99なら

コード:

struct A aa = {
    .a1 = 10,
    .a2 = 20
};
のように書けますが、これはC++には実装されていませんから、残念ながらできません。
一つの案ですが、どうしてもメンバ変数をconstにしたくて、しかもメンバ変数名を指定しつつ初期値を書きたいなら、mapをコンストラクタに渡す、なんていうのはどうでしょうか。

コード:

#include <iostream>
#include <map>
#include <string>
#include <boost/any.hpp>

struct A
{
    const int a1;
    const std::string a2;

    A(const std::map<std::string, boost::any>& init_values)
        : a1(boost::any_cast<int>(init_values.at("a1"))),
          a2(boost::any_cast<std::string>(init_values.at("a2")))
    {}
};

int main()
{
    A aa = {{
        {"a1", 10},
        {"a2", std::string("foo")}
    }};
    std::cout << aa.a1 << ", " << aa.a2 << std::endl;
}

zxzy

Re: メンバ変数のconst

#6

投稿記事 by zxzy » 12年前

softya(ソフト屋)さん、loweさん、beatleさん 回答ありがとうございます。

softya(ソフト屋)さんの方法で配列の場合でも初期化することが出来ました。

コード:

A a[3] = {A(10,2), A(5,5), A(3,2)};
と書けばよかったのですね

コード:

A a[3]{(10,2), (5,5), (3,2)};

コード:

A a[3](3,2);
とかかいてみてエラーだったのでできないと思いました。

C#なら代入のような書き方もできるのですか。
「C#なら出来ると思うのですが、C++/CLIは分かりません。」と書かれたのが
C#もC++/CLIも.NET系はわからないではなく、
C#では書けるがC++/CLIになるとわからないという
意味で書かれたのでしたら、
今回はC#でなくC++で作るため使えませんが気になるので
C#だとどうなるのかを教えてもらえますか?


loweさんのplacement newを使う方法でもできました。
こうすればnewを使っても->じゃなくて.を使えるのですね。

コード:

 new( a + 0 ) A( 10 );
を並べるだけなので見やすいですがこの方法でも

コード:

 new( a + 0 ) A( 10 , 10 , 10 , 10 );
のように引数に並べて書くことになりますね。


beatleさん
Cでは書けたのですか。
しかし、クラスを使うためC++なんです。
せっかく代案をだしてくれたのですが、複雑そうなので今回は
代入のように書くのは諦め、
softya(ソフト屋)さんの方法かloweさんの方法にすることにします。

ありがとうございました。



もう一つ聞きたいのですが

intで宣言したものをあとからconstに変更する
とかいう裏技があったり・・はしませんか?

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#7

投稿記事 by nullptr » 12年前

zxzy さんが書きました: loweさんのplacement newを使う方法でもできました。
こうすればnewを使っても->じゃなくて.を使えるのですね。
はい、placement newに渡しているのはポインタではなく参照だからです。

コード:

 new( a + 0 ) A( 10 );
を並べるだけなので見やすいですがこの方法でも

コード:

 new( a + 0 ) A( 10 , 10 , 10 , 10 );
のように引数に並べて書くことになりますね。
そもそも、C++ではconstメンバというのは基本的にコンストラクタの“{”まででしか初期化できませんから、不可能です。
(まぁ無理やり変更することもできなくはないですが、それでは定数の意味を成しません。

intで宣言したものをあとからconstに変更する
とかいう裏技があったり・・はしませんか?
聞いたことないですが多分無いんじゃないでしょうか・・・const intとintは別物でしょうから
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#8

投稿記事 by softya(ソフト屋) » 12年前

リクエストのものです。VC#2010以降限定。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CScon
{
    class A
    {
        readonly int a;
        readonly int b;
        public A(int arg_a=3, int arg_b=3)
        {
            a = arg_a;
            b = arg_b;
        }
        public void print()
        {
            Console.Write("{0:d},{1:d}\n", a, b);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A[] array = new A[3] { new A(0, 1), new A(arg_b: 2, arg_a: 15), new A(arg_b: 4, arg_a: 7) };

            for (int i = 0; i < 3; i++)
            {
                array[i].print();
            }
            
        }
    }
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

zxzy

Re: メンバ変数のconst

#9

投稿記事 by zxzy » 12年前

loweさん

回答ありがとうございます。
やっぱりintをconst intにするなんて無理ですよね。


softya(ソフト屋) さん

C#版ありがとうございます。
constじゃなくread onlyを使うことでコンストラクタで
初期化できるようにしているのですね。

しかし、私がしたかったことは、

コード:

a = 10;
b = 20;
:
:
のように定数に直接数字をいれ、見やすくしたかったのです。

教えていただいた方法では

コード:

a = arg_a;
b = arg_b;
と仮引数を代入し、

コード:

A[] array = new A[3] { new A(0, 1), new A(arg_b: 2, arg_a: 15), new A(arg_b: 4, arg_a: 7) };
のように数字はコンストラクタの実引数に書いています。



配列の場合に引数ありコンストラクタを呼ぶ方法とC++で代入は無理とわかったので
これで解決としたいと思います。

回答していただいた方々ありがとうございました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#10

投稿記事 by softya(ソフト屋) » 12年前

zxzy さんが書きました: softya(ソフト屋) さん

C#版ありがとうございます。
constじゃなくread onlyを使うことでコンストラクタで
初期化できるようにしているのですね。

しかし、私がしたかったことは、

コード:

a = 10;
b = 20;
:
:
のように定数に直接数字をいれ、見やすくしたかったのです。
arg_b: 4
は代入文と同じなので

コード:

	A[] array = new A[3] {
		new A(0, 1),
		new A(
				 arg_a: 15
				,arg_b: 2
			),
		new A(
				 arg_b: 4
				,arg_a: 7
			)
	};
と書き並べれば同じだと思いますけど。

こう書けば、ほぼの狙い通りだと思うんですけどね。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CScon
{
    class A
    {
        readonly int a;
        readonly int b;
        public A(int a = 3, int b = 3)
        {
            a = a;
            b = b;
        }
        public void print()
        {
            Console.Write("{0:d},{1:d}\n", a, b);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A[] array = new A[3] { 
            				new A(
	            					a: 0,
    	        					b: 1
    	        				),
            				new A(
	            					a: 15,
    	        					b: 2
            					),
            				new A(
            						a: 7,
            						b: 4
            					)
            			};

            for (int i = 0; i < 3; i++)
            {
                array[i].print();
            }

        }
    }
}


by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#11

投稿記事 by ISLe » 12年前

loweさんのplacement newを使う方法ですが、Aがコンストラクタとデストラクタで何かゴニョゴニョする場合だと問題になりませんかね。
配列確保したときにデフォルトコンストラクタが呼ばれますけど、デストラクタが呼ばれないままplacement newで上書きしてしまいますよね。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#12

投稿記事 by nullptr » 12年前

ISLe さんが書きました:loweさんのplacement newを使う方法ですが、Aがコンストラクタとデストラクタで何かゴニョゴニョする場合だと問題になりませんかね。
配列確保したときにデフォルトコンストラクタが呼ばれますけど、デストラクタが呼ばれないままplacement newで上書きしてしまいますよね。
たぶんならないとおもいますよ。配列を確保していて、確かにデフォルトコンストラクタは呼ばれます。
が、別にAの配列じゃなくてchar[1000]とかでもいいわけです。この場合とにかく静的にメモリを確保する(追記:静的でなくてはならないわけではない)ことが重要なわけです。
デフォルトコンストラクタは前述のように“ダミー”です。そこでゴニョゴニョすることは想定してません、というかゴニョゴニョしたらそれこそダミーの意味がなくなります。あくまでも“Aのn個分のメモリを確保ね”とかけるように何も値の無いAをつくって上書きできるようにしてるだけですから。
デストラクタですが、ゴニョゴニョする場合は明示的に呼び出せばよい話です。今回は定数のお話なので触れませんでしたが。

おかしな事言ってたらすみません。
最後に編集したユーザー nullptr on 2012年3月08日(木) 11:01 [ 編集 2 回目 ]
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#13

投稿記事 by ISLe » 12年前

>loweさん

それならダミーのコンストラクタの中身が空であることが重要ですよね。
ですが、わざわざ“ダミー”についてはどうでもよいというふうに断っていますよね。
なのでこのスレを読んだ人が「クラスの配列を宣言してplacement newで上書きするのはスタンダード」と誤解する可能性があるのではないかと思ったという次第です。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#14

投稿記事 by nullptr » 12年前

>ISLeさん
なるほど(´・ω・`)
定数の話だったので「どうでもいい」と表現したせいで誤解を招きそうですね・・
配慮が足りませんでした・・・すみません
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: メンバ変数のconst

#15

投稿記事 by GRAM » 12年前

lowe さんが書きました: 別にAの配列じゃなくてchar[1000]とかでもいいわけです。とにかく静的にメモリを確保することが重要なわけです。
横槍をスミマセンがそれは微妙です。
アライメントの問題があるので微妙というより基本的にはNGだと思います。
もちろんヒープから生のメモリを確保するのなら、そもそもデフォルトコンストラクタの問題も発生しないのですが。
ついでに空のコンストラクタを作ってしまうと、本来引数なしのコンストラクタを使いたくないのに
コンストラクタの引数なしでインスタンスが作成できてしまうようになるという問題も起きると思います。

結局、この場合プレースメントnew使えるのは
①そのクラスでユーザー定義の引数なしコンストラクタを必要としない。
②空の引数なしコンストラクタを作成することにより、引数なしでインスタンスが作成されてしまうことを許容する

ということですかね?
ところで、元も子もないことを言いますが、そもそも質問者さんは配列を使わないといけないのでしょうか?
配列にこだわらなくてstd::vector等を使ってもいいのなら、
reserveしてpush_backで加えていければほぼ要求通りのことが無理なくできると思うのですが・・・。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#16

投稿記事 by nullptr » 12年前

GRAM さんが書きました:
lowe さんが書きました: 別にAの配列じゃなくてchar[1000]とかでもいいわけです。とにかく静的にメモリを確保することが重要なわけです。
横槍をスミマセンがそれは微妙です。
アライメントの問題があるので微妙というより基本的にはNGだと思います。
すみません、アライメントの問題がある、とはどういうことでしょうか?
ついでに空のコンストラクタを作ってしまうと、本来引数なしのコンストラクタを使いたくないのに
コンストラクタの引数なしでインスタンスが作成できてしまうようになるという問題も起きると思います。
先の例はplacement newを使うという前提で組んでます、その際メモリをAの配列で確保するためにはデフォルトコンストラクタがいります。「引数なしのコンストラクタを使いたくない」などという条件は質問者様はおっしゃっていないですし。あと引数なしのコンストラクタを使いたくないのならcharとかなんなりでメモリ確保すればいいと思いますし(アライメントの問題というのを知りませんが・・・)。
結局、この場合プレースメントnew使えるのは
①そのクラスでユーザー定義の引数なしコンストラクタを必要としない。
②空の引数なしコンストラクタを作成することにより、引数なしでインスタンスが作成されてしまうことを許容する
ということですかね?
①は「この場合」では無いかと。②は確かにそうです。これもちゃんとハッキリ書くべきでした。本当にすみません。


【追記】
すみません今調べて知りました。m(_ _)m

【更に追記】
あとこれ「アライメント」ですねすみませんm(_ _)m
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

zxzy

Re: メンバ変数のconst

#17

投稿記事 by zxzy » 12年前

解決済みとしたのに返信をいただきありがとうございます。


softya(ソフト屋)さん

コード:

A[] array = new A[3] { new A(0, 1), new A(arg_b: 2, arg_a: 15), new A(arg_b: 4, arg_a: 7) };

コード:

A[] array = new A[3] { 
		new A(
			a: 0,
			b: 1
		),
		new A(
			a: 15,
			b: 2
		),
		new A(
			a: 7,
			b: 4
		)
	};
のようにかけば代入文のようにかけますね。

気づきませんでした。

ところで

コード:

        public A(int a = 3, int b = 3)
        {
            a = a;
            b = b;
        }
この部分のa=a;と書いても大丈夫なのでしょうか?
仮引数に代入はないし、readonly int のaにreadonly intのaを
入れるのも無意味なのでreadonly intのaに仮引数のaを入れるというのは
考えるとわかるのですがプログラムでこう書いても大丈夫なのでしょうか?
(エラーが出ないのかということと後で見た時なにしてるのかわかりづらく
なるため一般にこう書くべきでないなどの面で)


loweさん、ISLeさん、GRAMさん

複雑で完全に理解ができていないのですが
引数なしコンストラクタを必要としない場合でplacement newを絶対に使い
ダミーコンストラクタで引数なしのコンストラクタでインスタンスが作成
されてもいいのならplacement newを使ってもいいのでしょうか?

デストラクタについてですがplacement newを使うと静的になるので

コード:

A a[3];
new( a + 0 ) A( 10 );
new( a + 1 ) A( 20 );
new( a + 2 ) A( 30 );
とすると

コード:

static A a[3];
の宣言したようになり、プログラム終了時までデストラクタが呼ばれなくなるので
自分で呼ぶ必要があるということでいいですか?


アライメントの問題というのがよくわからないのですが、
ダミーコンストラクタによるインスタンス生成時にクラスAの領域が確保されているので
placement newを使っても問題無いように思うのですけど。
placement newを使うとパディングが変わりAのサイズが変わったりするのですか?

また、ヒープから確保するとなると

コード:

A* a[2];
a[0] = new A(10);
a[1] = new A(20);
となってしまいますよね。
なので今回はplacement newを教えていただいたのだと思います。


配列を使わないといけないのかということですが、
Aのインスタンスを複数生成し、そのどれかのメンバの値を取り出す、書きこむ
ということをするときに配列でないと

コード:

int x,y;
A ai1;
A ai2;
/* ・・・ */
if(x == 1){
	ai1.abc = 0;
	y = ai1.cint;
}else if(x == 2){
	ai2.abc = 0;
	y = ai2.cint;	
}
のようにif文を書く必要があり、
配列にすると

コード:

int x,y;
A ai[2];
/* ・・・ */
ai[x-1].abc = 0;
y = ai[x-1].cint;
のようにかけるため配列を選びました。

std::vectorは使ったことがないのですが
配列より不便になるような欠点はないのですか?
できれば、上記の配列の例をstd::vectorにしたものを書いてもらえませんか?

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#18

投稿記事 by nullptr » 12年前

zxzy さんが書きました: 引数なしコンストラクタを必要としない場合でplacement newを絶対に使い
ダミーコンストラクタで引数なしのコンストラクタでインスタンスが作成
されてもいいのならplacement newを使ってもいいのでしょうか?
引数無しでインスタンス生成ができてしまってもプログラムに支障をきたさない設計にすれば問題ないと思います。
デストラクタについてですがplacement newを使うと静的になるので

コード:

A a[3];
new( a + 0 ) A( 10 );
new( a + 1 ) A( 20 );
new( a + 2 ) A( 30 );
とすると

コード:

static A a[3];
の宣言したようになり、プログラム終了時までデストラクタが呼ばれなくなるので
自分で呼ぶ必要があるということでいいですか?
えーと、どうやらplacement newをご存じないようなので、簡単に説明します。
placement new(配置new)とは、名の通り静的なメモリ領域にインスタンスを配置します(追記:この場合はという話で、動的なメモリに確保することもできます)。つまるところ、決してインスタンスが静的になるわけではありません。

コード:

static A a[3];
は違います。確保されたメモリ領域の寿命は確かにstaticと同じですが(追記:例の場合は)、そこに配置されたインスタンスの寿命は同じとは限りません。
そうですね・・・例えばAのインスタンスを入れた後、自分でデストラクタと開放処理を行なえば違うAを同じメモリに配置することも可能ということです。
プログラム終了時までデストラクタが呼ばれなくなるので
自分で呼ぶ必要があるということでいいですか?
placement newはあくまでも「メモリに配置する」だけなのでデストラクタ(のあとで裏で行なわれているメモリの開放処理)を呼び出す必要が無いのですが、もしデストラクタでA内で動的に確保した領域を開放する処理などがあった場合はデストラクタを呼び出したくなりますよね?また、先ほど言ったように領域を使いまわす際は自分でデストラクタと開放処理を行なわなくてはならないということです。
A内で動的な確保が行なわれておらず、メモリを使いまわすつもりがないのなら呼び出す必要はありません。

アライメントの問題というのがよくわからないのですが、
ダミーコンストラクタによるインスタンス生成時にクラスAの領域が確保されているので
placement newを使っても問題無いように思うのですけど。
placement newを使うとパディングが変わりAのサイズが変わったりするのですか?
「ダミーコンストラクタによるインスタンス生成時にクラスAの領域が確保されているので」が間違いですね。前述のように、既にあるメモリに配置されているだけで、「ダミーコンストラクタによるインスタンス生成時にクラスAの領域は確保されていません
この場合のアラインメントの問題についてですが、私の言った「char[1000]でもいい」というのが間違いです。charの配列として確保された領域にAを配置すると不具合が起きる場合があるらしいです。
また、ヒープから確保するとなると

コード:

A* a[2];
a[0] = new A(10);
a[1] = new A(20);
となってしまいますよね。
なので今回はplacement newを教えていただいたのだと思います。
そうですね・・・

とりあえず、描き忘れたことをまとめますと、
デメリット
・placement newを使う場合は引数なしでインスタンスが生成されることを認めなくてはならない

メリット
・配置newは早い
・->ではなく.が使える

注意点
・ダミーの初期化では何もしないこと




配列を使わないといけないのかということですが、
Aのインスタンスを複数生成し、そのどれかのメンバの値を取り出す、書きこむ
ということをするときに配列でないと

コード:

int x,y;
A ai1;
A ai2;
/* ・・・ */
if(x == 1){
	ai1.abc = 0;
	y = ai1.cint;
}else if(x == 2){
	ai2.abc = 0;
	y = ai2.cint;	
}
のようにif文を書く必要があり、
配列にすると

コード:

int x,y;
A ai[2];
/* ・・・ */
ai[x-1].abc = 0;
y = ai[x-1].cint;
のようにかけるため配列を選びました。

std::vectorは使ったことがないのですが
配列より不便になるような欠点はないのですか?
できれば、上記の配列の例をstd::vectorにしたものを書いてもらえませんか?
vectorを使って不便になる欠点はないと思います。

コード:

#include <vector>

int main(){
    using namespace std;
    int x, y;
    vector<A*> Vector;                                            // A型のポインタの動的配列
    vector<A*>::iterator Iterator_vector   // A型のポインタの動的配列のイテレータ

    Vector.push_back( new A );
    Vector.push_back( new A );

    // ~処理~

    Iterator_vector = Vector.begin();
    advance( Iterator_vector, x );
    (*Iterator_vector)->abc = 0;
    y = (*Iterator_vector)->cint;
    return 0;
};
最近VECTORを触ってないのですが多分こんなカンジかと・・・。
最後に編集したユーザー nullptr on 2012年3月08日(木) 11:03 [ 編集 1 回目 ]
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#19

投稿記事 by ISLe » 12年前

例えば以下のコードだと、

コード:

#include <iostream>
struct A {
	const int a;
	A()      : a(0) { std::cout << "A():" << a << std::endl; }
	A(int a) : a(a) { std::cout << "A():" << a << std::endl; }
	~A() { std::cout << "~A():" << a << std::endl; }
};
int main(void) {
	A a[1];          // メモリを確保して引数のないコンストラクタが呼ばれる
	new(&a[0]) A(1); // 引数のあるコンストラクタが呼ばれてメモリを上書き
	return 0;
}                    // デストラクタが呼ばれるが…
A():0
A():1
~A():1
と表示されます。
コンストラクタが2回に対してデストラクタは1回です。

コンストラクタとデストラクタが対称性を持って設計されたクラス(デストラクタが呼ばれないと困るクラス)だと問題になります。

しかもデストラクタは一時変数を解体するときに呼ばれたもので実のところプレースメントnewに対応するものではありません。
メモリ領域を使い回すためnewに対してdeleteを徹底すれば意図しない二重解体という不具合に突き当たります。
静的な変数でも解体のタイミングが異なるだけで同様の問題を含みます。

コンストラクタとデストラクタは対称性を持っていると考えるべきものだと思います。
今回引数の無いコンストラクタが空であれば問題にならないとしても、受け入れることで、将来的にこの手の問題に対して鈍感になってしまうかもしれません。

アラインメントや意図しない解体の問題を避けるには、

コード:

A *p = (A *)malloc(sizeof(A) * 10);
というふうにメモリを確保してpが指すアドレス境界に対してプレースメントnewを使えば良いと思います。

コンストラクタがメモリを確保するのではありませんし、デストラクタがメモリを解放するのでもありません。
コンストラクタはメモリを初期化するために呼び出されるものです。
メモリの確保自体はクラスでもintでも同じです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#20

投稿記事 by softya(ソフト屋) » 12年前

失礼しました。

コード:

        public A(int a = 3, int b = 3)
        {
            this.a = a;
            this.b = b;
        }
書き間違いです。 確認せず直したので申し訳ないです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

zxzy

Re: メンバ変数のconst

#21

投稿記事 by zxzy » 12年前

loweさん、 ISLeさん

placement new について説明していただきありがとうございます。

インスタンスが静的になるわけではないということは

コード:

void function(){
	
	A a[3];
	new( a + 0 ) A( 10 );
	new( a + 1 ) A( 20 );
	new( a + 2 ) A( 30 );

	/* ... */
	
}

int main(){

	function();
	function();

	return 0;
}
のようにplacement newを使う関数を複数呼ぶ場合はどうなるのでしょうか?
A a[3];でplacement new用に確保した領域にplacement newで配置し
その領域を静的にすると、2回目以降のfunction()でA a[3];が来た時staticが
ついていないのでまた、別の場所に確保することになるように思うのですが
staticと同様に2回目以降A a[3];で新たに確保しないようにできているのでしょうか?


アライメントの問題については
メモリを確保するだけでも配置しようとしている型で確保すれば問題ないのですね。


vectorにすることの欠点はないのですか。しかし、やはり配列より複雑で長くなりますね。
それと.ではなく->を使うことになるのですね。
->を使うのなら

コード:

A* a[2];
a[0] = new A(10);
a[1] = new A(20);
にしてヒープから確保する方が楽にできる気がします。
vectorがよくわかってないのですが->じゃなく.にする方法あったりはしないですか?

ですが、実装方法の一つとして知っておきたいと思います。

ところで、GRAMさんは「reserveしてpush_backで加えていければ」
と書かれていましたがreserveは使わなくても大丈夫なのでしょうか?

それと
7行目の

コード:

vector<A*>::iterator Iterator_vector   // A型のポインタの動的配列のイテレータ
にセミコロンがないのと
19行目

コード:

};
にセミコロンがあるのは単なるミスですよね?
vectorがある以外コンソールのC++と同じなので




ISLeさん

mallocを使うのですか。
C++だとコンストラクタ、デストラクタがあるnewを使うべきで
mallocは使うべきでないとあちこちで見たのでC++でmallocは
使うことはないと思っていましたがこういうコンストラクタを
必要とせずメモリの確保だけしたいときに使えるのですね。

ありがとうございました。


softya(ソフト屋)さん

やっぱり

コード:

a = a;
と書くのは間違いでしたか。

C#で書くときの参考にしたいと思います。ありがとうございました。

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: メンバ変数のconst

#22

投稿記事 by GRAM » 12年前

zxzy さんが書きました:vectorにすることの欠点はないのですか。しかし、やはり配列より複雑で長くなりますね。
それと.ではなく->を使うことになるのですね。
・・・
ところで、GRAMさんは「reserveしてpush_backで加えていければ」
と書かれていましたがreserveは使わなくても大丈夫なのでしょうか?
自分ならこう書くという書き方を提示します。
参考にどうぞ
C++で好きなように書けるような環境だとしたら(そこそこ速いPCで動かして、十分なリソースがあるなら)、
これが最善だと僕は信じています。

コード:

#include <iostream>
#include <vector>


using namespace std; //楽します。スミマセンw

class Hoge
{
public:
	Hoge( int n)
		: num(n)
	{
		cout << "コンストラクタが呼ばれました。値は" << num << "です" << endl;
	}
	void ShowNum() const
	{
		cout << num << endl;
	}
private:
	const int num;
};

int main()
{
	vector< Hoge > arrayHoge;			//Hogeクラスを格納するvector
	arrayHoge.reserve(5);				//vectorに確保されているメモリを最低でも5以上にする
	arrayHoge.push_back( Hoge(1) );		//あとは入れていくだけ
	arrayHoge.push_back( Hoge(2) );
	arrayHoge.push_back( Hoge(3) );
	arrayHoge.push_back( Hoge(4) );
	arrayHoge.push_back( Hoge(5) );


	cout << "-------------------" << endl;
	for( int i = 0; i < 5; ++i )
	{
		arrayHoge[i].ShowNum();
	}
}
ややこしいという話ですが、自分には到底そうは思えません。
このコンベンションがややこしくないという理由はいくらでもあります。

①Hogeクラスに何も手を加えない。(空のコンストラクタの制約もない)
②実行結果からわかる通り、引数ありのコンストラクタ呼び出しは1回ですむ。(後述のように、コピーコンストラクタは呼ばれてしまう)
  またデフォルトコンストラクタは1度も呼ばれていない。そう。たったの1度も。
③メモリのことを考える必要から解放されている。(reserveで要素数をいれるということを除けば。しかしこれすらも、効率を気にしなければ省ける)
④プレースメントnewという、テクニカルなことをする必要がない。
  (ヒープから確保するならなおさらテクニカル。解法や、デストラクタの明示的呼び出し等。またアライメントのことを考える必要も全くない)
⑤この方法ではデストラクタのことを考える必要もない。vectorが破棄されるときに自動で呼ばれる。
⑥そもそも、この方法のデメリットというのがあまり思いつかない。
⑦ただし、あえてデメリットを挙げるとしたら、コピーコンストラクタが必要になる。それを作りたくないなら確かにplacement_newを使うしかないのかもしれない
⑧そもそもこのコードを見て、複雑だと感じるでしょうか?自分はふつうに読めるコードだと思います。


という理由で、あくまで自分ならですが、この方法を使うと思われます。

さて、std::vectorですが、 確保しているメモリが連続していなくてはならなくて
しかも、償却定数時間で後ろに要素を付け加えていくことができるわけですから、
考えうる限りでは、あらかじめ確保しているメモリよりも多くの要素を追加しようとすると、メモリの再確保が行われると考えられます。
これも自分の知る限りはですが、償却定数時間にしようとすると、少なくとも現在の要素のx倍(xはなんでもいいが2が使われるのかな?)
のメモリを確保しなくてはなりません。(そうすることで、要素を無限に加えていったときにメモリの再確保の回数がいずれなくなる。)

となると。です。reserveしなくてもいいのですが、reserveした方が、圧倒的に効率がよくなることがあります。
たとえばこの例だとしても、「仮に」最初にメモリが全く確保されていなければ(これはわからない)
push_backのたびに、1回目(メモリ1確保) 2回目(メモリ2確保) 3回目(メモリ4確保) 4回目( メモリ確保なし) 5回目(メモリ8確保)
・・・となって、4回もメモリの再確保が行われていたとしても文句は言えないわけです。
さて、もっと大きなサイズならどうなるのでしょうね?
・・・という理由でreserveします。


最後にoperator.か->かという話ですが、見ての通りです。vectorは場合によってはoperator[]でアクセスしたほうがいい時もあります。
(ループ途中で要素が追加されるときなど)
iteratorしか使えないわけでもないので、そこらへんはご自由に。(自分は結構使い分ける人ですが)

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#23

投稿記事 by ISLe » 12年前

プレースメントnewを使うときに変な誤解をするといけないと思って指摘しただけなのに、横道が伸びてしまって申し訳ないです。
zxzy さんが書きました:A a[3];でplacement new用に確保した領域にplacement newで配置し
その領域を静的にすると、2回目以降のfunction()でA a[3];が来た時staticが
ついていないのでまた、別の場所に確保することになるように思うのですが
staticと同様に2回目以降A a[3];で新たに確保しないようにできているのでしょうか?
一時変数の配列aを宣言することで(これ以前に)確保されたメモリを引数の無いコンストラクタで初期化します。
つまり宣言しただけでクラスオブジェクトが構築されます。

プレースメントnewはコンストラクタを呼び出して、指定したアドレスを先頭としたメモリを初期化するだけです。
そして初期化によって上書きしてしまうわけですから、既にそこにあったオブジェクトは破壊されます。

一時変数の配列aはfunction関数の終わりでライフサイクルを抜けます。
それと同時にデストラクタが呼び出されクラスオブジェクトが解体されます。
そのあとメモリも解放されます。
破壊されたオブジェクトがライフサイクルを終えて解体されるという状況を、わたしはバグだと思います。
zxzy さんが書きました:mallocを使うのですか。
C++だとコンストラクタ、デストラクタがあるnewを使うべきで
mallocは使うべきでないとあちこちで見たのでC++でmallocは
使うことはないと思っていましたがこういうコンストラクタを
必要とせずメモリの確保だけしたいときに使えるのですね。
new/deleteは任意のタイミングで構築/解体をするための演算子です。
new/deleteだけがコンストラクタ/デストラクタを呼ぶのではありません。
上に書いたように、クラス型の変数を宣言すれば自動的にコンストラクタが呼ばれますし、ライフサイクルを抜けると自動的にデストラクタが呼ばれます。
mallocはどんなオブジェクトに対してもアラインメントの問題が起きないようにアドレス境界を調整してくれます。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#24

投稿記事 by nullptr » 12年前

7行目の

コード:

vector<A*>::iterator Iterator_vector   // A型のポインタの動的配列のイテレータ
にセミコロンがないのと
19行目

コード:

};
にセミコロンがあるのは単なるミスですよね?
vectorがある以外コンソールのC++と同じなので
はい、確認しないで送信してました、すみませんm(_ _;)m

zxzy さんが書きました: vectorがよくわかってないのですが->じゃなく.にする方法あったりはしないですか?
ああ、

コード:

#include <vector>
 
int main(){
    using namespace std;
    int x, y;
    vector<A> Vector;                              // A型の動的配列
 
    Vector.push_back( A(1) );
    Vector.push_back( A(2) );
 
    // ~処理~
 
    Vector[x-1].abc = 0;
    y = Vector[x-1].cint;
    return 0;
}
みたいにすればできると思いますが・・・ただこれだと動的な確保が面倒になりませんかね?この場合のAがどういう使い方をされるかによりますが
GRAMさんがほとんど説明されてますので特に説明はいらないと思いますが、vectorを使えばplacement newよりいいことはたくさんあります。(placement newは実際はどちらかっていうとメモリプールとかに使うテクニックなんで)



/* ここから余談 */
► スポイラーを表示
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#25

投稿記事 by tk-xleader » 12年前

>ISLeさん

コード:

#include <iostream>
struct A {
    const int a;
    A()      : a(0) { std::cout << "A():" << a << std::endl; }
    A(int a) : a(a) { std::cout << "A():" << a << std::endl; }
    ~A() { std::cout << "~A():" << a << std::endl; }
};
int main(void) {
    A a[1];          // メモリを確保して引数のないコンストラクタが呼ばれる
    new(&a[0]) A(1); // 引数のあるコンストラクタが呼ばれてメモリを上書き
    return 0;
}                    // デストラクタが呼ばれるが…
トピックの流れからいうと、無茶苦茶どうでもいいことなんですが、このコードは厳密には規格違反(たぶん、いわゆる「鼻から悪魔」のコード)です。
なぜなら、構造体Aが定数修飾された非静的メンバを持っているからです。メンバ変数a が単なるint型なら全く問題は無いのですが…

zxzy

Re: メンバ変数のconst

#26

投稿記事 by zxzy » 12年前

GRAMさん、ISLeさん、loweさん返信ありがとうございます。
GRAM さんが書きました:
ややこしいという話ですが、自分には到底そうは思えません。
このコンベンションがややこしくないという理由はいくらでもあります。

①Hogeクラスに何も手を加えない。(空のコンストラクタの制約もない)
②実行結果からわかる通り、引数ありのコンストラクタ呼び出しは1回ですむ。(後述のように、コピーコンストラクタは呼ばれてしまう)
  またデフォルトコンストラクタは1度も呼ばれていない。そう。たったの1度も。
③メモリのことを考える必要から解放されている。(reserveで要素数をいれるということを除けば。しかしこれすらも、効率を気にしなければ省ける)
④プレースメントnewという、テクニカルなことをする必要がない。
  (ヒープから確保するならなおさらテクニカル。解法や、デストラクタの明示的呼び出し等。またアライメントのことを考える必要も全くない)
⑤この方法ではデストラクタのことを考える必要もない。vectorが破棄されるときに自動で呼ばれる。
⑥そもそも、この方法のデメリットというのがあまり思いつかない。
⑦ただし、あえてデメリットを挙げるとしたら、コピーコンストラクタが必要になる。それを作りたくないなら確かにplacement_newを使うしかないのかもしれない
⑧そもそもこのコードを見て、複雑だと感じるでしょうか?自分はふつうに読めるコードだと思います。
GRAMさんの書いたコードをみるとそこまで複雑ではないですね。
loweさんのコードで複雑に感じたのはメンバに代入する部分も
書いてもらったためイテレータなども組合わさっていたためですね。

vectorのポインタ イテレータ テンプレートと見慣れないものが
たくさんあったためなので慣れればそうでもないかなと思えました。

placement new や newのほうがテクニカルなのですか。
vectorのほうが高度でテクニカルだと思ってました。

コピーコンストラクタはクラスをコピーするときに使うので、
デフォルト以外を使うなら他の方法でも必要な気がします。
教えていただいたコードにも書かれていなく普通にコピーするだけのときは
書かなくてもいいようですし特にデメリットというほどでもないですね。


GRAM さんが書きました: さて、std::vectorですが、 確保しているメモリが連続していなくてはならなくて
しかも、償却定数時間で後ろに要素を付け加えていくことができるわけですから、
考えうる限りでは、あらかじめ確保しているメモリよりも多くの要素を追加しようとすると、メモリの再確保が行われると考えられます。
これも自分の知る限りはですが、償却定数時間にしようとすると、少なくとも現在の要素のx倍(xはなんでもいいが2が使われるのかな?)
のメモリを確保しなくてはなりません。(そうすることで、要素を無限に加えていったときにメモリの再確保の回数がいずれなくなる。)

となると。です。reserveしなくてもいいのですが、reserveした方が、圧倒的に効率がよくなることがあります。
たとえばこの例だとしても、「仮に」最初にメモリが全く確保されていなければ(これはわからない)
push_backのたびに、1回目(メモリ1確保) 2回目(メモリ2確保) 3回目(メモリ4確保) 4回目( メモリ確保なし) 5回目(メモリ8確保)
・・・となって、4回もメモリの再確保が行われていたとしても文句は言えないわけです。
さて、もっと大きなサイズならどうなるのでしょうね?
・・・という理由でreserveします。
reserveがあったほうがいい理由がわかりました。
丁寧な説明ありがとうございます。


GRAM さんが書きました: 最後にoperator.か->かという話ですが、見ての通りです。vectorは場合によってはoperator[]でアクセスしたほうがいい時もあります。
(ループ途中で要素が追加されるときなど)
iteratorしか使えないわけでもないので、そこらへんはご自由に。(自分は結構使い分ける人ですが)
operatorがiteratorのようなものかと思って調べましたが普通に演算子のことなんですね。
例のプログラムを見たところ[]のほうがわかりやすいなと思えました。


ISLe さんが書きました:プレースメントnewを使うときに変な誤解をするといけないと思って指摘しただけなのに、横道が伸びてしまって申し訳ないです。
いえ、使ったことのないものだったので詳しい説明や注意などを聞けて勉強になります。
質問してばかりですが毎回丁寧に回答していただきありがたいです。

ISLe さんが書きました: 一時変数の配列aを宣言することで(これ以前に)確保されたメモリを引数の無いコンストラクタで初期化します。
つまり宣言しただけでクラスオブジェクトが構築されます。

プレースメントnewはコンストラクタを呼び出して、指定したアドレスを先頭としたメモリを初期化するだけです。
そして初期化によって上書きしてしまうわけですから、既にそこにあったオブジェクトは破壊されます。

一時変数の配列aはfunction関数の終わりでライフサイクルを抜けます。
それと同時にデストラクタが呼び出されクラスオブジェクトが解体されます。
そのあとメモリも解放されます。
ということはfunction関数を抜けた時点でplacement newで配置したものはなくなるので
2回目以降に1回目のfunction関数で変更した値は残っていないんですね。
なら、静的というのはどういうことですか?静的というとプログラムの最後までメモリが確保され
残っているものだと思っていたのですが、これが間違いですか?
ISLe さんが書きました: 破壊されたオブジェクトがライフサイクルを終えて解体されるという状況を、わたしはバグだと思います。
placement newを使う以上しょうがないことだと思うのですが
placement new自体を使わないほうがいいということでしょうか?

lowe さんが書きました: ああ、

コード:

#include <vector>
 
int main(){
    using namespace std;
    int x, y;
    vector<A> Vector;                              // A型の動的配列
 
    Vector.push_back( A(1) );
    Vector.push_back( A(2) );
 
    // ~処理~
 
    Vector[x-1].abc = 0;
    y = Vector[x-1].cint;
    return 0;
}
みたいにすればできると思いますが・・・ただこれだと動的な確保が面倒になりませんかね?この場合のAがどういう使い方をされるかによりますが
GRAMさんがほとんど説明されてますので特に説明はいらないと思いますが、vectorを使えばplacement newよりいいことはたくさんあります。(placement newは実際はどちらかっていうとメモリプールとかに使うテクニックなんで)
vectorはpush_backで動的に要素を追加していけるのではないのですか?
placement newはメモリプールに使うものだったのですか。

lowe さんが書きました: boostライブラリにptr_vectorなんてものがあります。
かなり便利なんで私も多用しちゃってるんですが、、、

それを使うとこんな感じになります。

コード:

#include <boost/ptr_container/ptr_vector.hpp>
 
int main(){
    using namespace std;
    int x, y;
    boost::ptr_vector<A> Vector;     // A型の「ポインタの」動的配列
 
    Vector.push_back( new A(1) );   // ポインタを入れる
    Vector.push_back( new A(2) );
 
    // ~処理~
 
    Vector[x-1].abc = 0;                   // .演算子でアクセスできる
    y = Vector[x-1].cint;
    return 0;
}
これを使えば.演算子でアクセスできますし(イテレータでもoperator[]を使った場合でも)、要素を消すときにいちいちdeleteを呼ぶ必要もなく、newでつっこめるんで動的確保も楽です。
・・・まぁboostを導入しなくちゃいけなかったり若干注意しなくてはならない点もあったりするんでvectorを知らないならオススメはしませんが、参考までに。
vectorは名前は聞いたことがあったのですがboostは全く聞いたことがありませんでした。
調べてみたらVC++とは別にインストールがいるようですね。
便利そうですがC++ですらまだ知らない部分が多く回答いただくたびに知らない用語を
いろいろ調べているレベルなのでboostの導入は今回しないでおきます。
最初の頃beatleさんが代案としてだしてくれたmapの方法にあったboostというものも
これなのですね。みたことがないincludeだなと思っただけで流してしまいましたが。。



少し話がずれますが
GRAMさんが書いてくれたコード
GRAM さんが書きました:

コード:

#include <iostream>
#include <vector>
 
 
using namespace std; //楽します。スミマセンw
 
class Hoge
{
public:
    Hoge( int n)
        : num(n)
    {
        cout << "コンストラクタが呼ばれました。値は" << num << "です" << endl;
    }
    void ShowNum() const
    {
        cout << num << endl;
    }
private:
    const int num;
};
 
int main()
{
    vector< Hoge > arrayHoge;           //Hogeクラスを格納するvector
    arrayHoge.reserve(5);               //vectorに確保されているメモリを最低でも5以上にする
    arrayHoge.push_back( Hoge(1) );     //あとは入れていくだけ
    arrayHoge.push_back( Hoge(2) );
    arrayHoge.push_back( Hoge(3) );
    arrayHoge.push_back( Hoge(4) );
    arrayHoge.push_back( Hoge(5) );
 
 
    cout << "-------------------" << endl;
    for( int i = 0; i < 5; ++i )
    {
        arrayHoge[i].ShowNum();
    }
}
これの実行結果をみようと実行しようとしたのですが
最初見たときVC++を入れてないPCで見たので
Cygwinで実行しようとしました。
すると、エラーが出てコンパイルできませんでした。

いろいろ変えてみたりして
・const int num;をint num;にしたとき
・vector使わずにHoge h(1);としたとき
はコンパイルが通りました。
通ったはいいがダメだった理由はわかりません。

このままだとコンパイルが通らない理由はなんなのでしょう?
VC++でしか使えない書き方ではないと思うのですけど・・・

エラーメッセージは
~/vector.tcc:238: error: non-static const member `const int Hoge::num', can't use default assignment operator
です。

VC++2010を入れている方で後で試したらそのままのコピペで
動作しました。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#27

投稿記事 by tk-xleader » 12年前

zxzy さんが書きました: ~/vector.tcc:238: error: non-static const member `const int Hoge::num', can't use default assignment operator
「std::vector::push_back関数が内部で代入演算子を使っているのに、適切な代入演算子が定義されていない」という意味のエラーですが、Hoge::numがconstなので、自明な代入演算子(要するに、メンバ変数の一つ一つにコピー演算子を適用していく代入演算子のこと。必要に応じてコンパイラが自動でメンバに加える。)が定義できないから起こっているのです。
ところが、適切な代入演算子を定義する方法が今回はありません。だから、Hogeをコンテナに入れることは実はあまりお勧めできません。どうがんばっても、Hogeをコンテナの要素としての要件を満たすように代入演算子を定義できませんから…

アバター
a5ua
記事: 199
登録日時: 13年前

Re: メンバ変数のconst

#28

投稿記事 by a5ua » 12年前

コード:

class Hoge
{
public:
    Hoge( int n)
        : num(n)
    {
        cout << "コンストラクタが呼ばれました。値は" << num << "です" << endl;
    }
    void ShowNum() const
    {
        cout << num << endl;
    }

	// コンパイラが生成するデフォルトの代入演算子
	Hoge &operator=(const Hoge &other)
	{
		// num は const なので代入できない
		num = other.num;
		return *this;
	}
private:
    const int num;
};
std::vector<T> における T は、コピーコンストラクタと代入演算子を持っていなければいけません。
代入演算子を省略した場合に、コンパイラが自動で生成する代入演算子は、
すべてのメンバーを一つずつ代入するので、エラーとなります。

VC++ の場合は、reserve や push_back が Hoge::operator= をたまたま使わないので、
Hoge::operator= が無視されていると考えられます。

Hoge::operator= が num を変更しない代入演算を行えば、コンパイルできますが、
それが、正しい実装であるかは、Hoge の設計者がよく考える必要があります。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#29

投稿記事 by tk-xleader » 12年前

a5ua さんが書きました:std::vector<T> における T は、コピーコンストラクタと代入演算子を持っていなければいけません。
それに加えて、ユーザー定義の型TをC++の標準コンテナに入れる場合、コピーコンストラクタと代入演算子の適用後、元のオブジェクトとの等値であるように(要するに、operator==で比較したときに真になるように)コピーコンストラクタと代入演算子を定義しないと、コンテナ自体がおかしくなる場合があります。
要するに、intやポインタ型のような型とできる限り同じように振舞うようにするのが、コンテナの要素としては理想の型であるということです。

ところが、今回のHoge型は、constなメンバを持っている為に、それを満たすように代入演算子が定義できないのですね。const修飾を無理やり外すと確実に未定義になりますからね…

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#30

投稿記事 by ISLe » 12年前

tkmakwins15 さんが書きました:>ISLeさん

コード:

#include <iostream>
struct A {
    const int a;
    A()      : a(0) { std::cout << "A():" << a << std::endl; }
    A(int a) : a(a) { std::cout << "A():" << a << std::endl; }
    ~A() { std::cout << "~A():" << a << std::endl; }
};
int main(void) {
    A a[1];          // メモリを確保して引数のないコンストラクタが呼ばれる
    new(&a[0]) A(1); // 引数のあるコンストラクタが呼ばれてメモリを上書き
    return 0;
}                    // デストラクタが呼ばれるが…
トピックの流れからいうと、無茶苦茶どうでもいいことなんですが、このコードは厳密には規格違反(たぶん、いわゆる「鼻から悪魔」のコード)です。
なぜなら、構造体Aが定数修飾された非静的メンバを持っているからです。メンバ変数a が単なるint型なら全く問題は無いのですが…
オブジェクトの構築・解体において定値(const)修飾に関して未定義の動作になるのは、
  • 定値オブジェクトが占有する領域を再使用する場合(メンバについては記述無し)
  • 定値修飾された非静的メンバを含むオブジェクトを再使用したとき、再使用する前に指していたポインタ・参照・名前を使ってアクセスした場合
です。
生存期間終わりの自動解体に関しては解体時に同じ型のオブジェクトであることを保証できなければ未定義となってます。
文面通りに解釈すれば未定義になるコードは含まれていないと言える気がするのですけど。

constを剥がして書き換えるのは未定義ですけど、ここでは関係無いと思います。


規格の話になったので補足しておきますけど、
プレースメントnewを使ってコンストラクタとデストラクタの数が合わなくなっても規格上は違反ではないのです。
規格に、再使用するときにデストラクタを明示的に呼び出さなくても良いがその場合デストラクタの副作用に依存したプログラムの動作は未定義、なんてふうに書かれているのですが、現実的な話としてデストラクタに副作用が無いと考えてコード書くのは良くないと思います。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#31

投稿記事 by ISLe » 12年前

zxzy さんが書きました:ということはfunction関数を抜けた時点でplacement newで配置したものはなくなるので
2回目以降に1回目のfunction関数で変更した値は残っていないんですね。
なら、静的というのはどういうことですか?静的というとプログラムの最後までメモリが確保され
残っているものだと思っていたのですが、これが間違いですか?
プレースメントnewで使用しているメモリはあくまでも関数内で宣言された一時変数に割り当てられたものですからね。

わたしは「静的」という単語は「静的な変数の生存範囲」のときにしか使ってないのですけど。
スレを読み直してみたところloweさんが「プレースメントnewは静的に配置される」と書かれてました。
これは間違いですね。
プレースメントnewは指定されたアドレスに上書きするだけですから動的に確保されたメモリにも使えます。
プレースメントnewを使うとメモリが再割当てされるわけではありません。
プレースメントnewで指定したアドレスのメモリ領域はそれが割り当てられた元のオブジェクトの生存期間に準じます。
zxzy さんが書きました:
ISLe さんが書きました:破壊されたオブジェクトがライフサイクルを終えて解体されるという状況を、わたしはバグだと思います。
placement newを使う以上しょうがないことだと思うのですが
placement new自体を使わないほうがいいということでしょうか?
規格上は違反ではないので分かってて使う分には良いのですけどね。
厳しい条件を忘れてうっかりバグを埋め込んでしまわないようになるべく使わないほうが良いと思います。
たいてい他にもっと良い方法があるはずです。
プレースメントnewを使うのはメモリプールを実装するときだけにしておいてください。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: メンバ変数のconst

#32

投稿記事 by nullptr » 12年前

ISLe さんが書きました: スレを読み直してみたところloweさんが「プレースメントnewは静的に配置される」と書かれてました。
これは間違いですね。
わたしがあげた例の話として「静的なメモリ領域にインスタンスを配置します」と言ったつもりでして、決して静的でなくてはならないと言ったつもりは無いのですが・・・・・。
まぁいずれにせよ誤解を招いたのは私の非ですね、zxzyさんすみませんでしたm(_ _)m

【追記】
すごく今更ですがそれらのところに動的な領域でもいい旨を書きました
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: メンバ変数のconst

#33

投稿記事 by GRAM » 12年前

>>tkmakwins15さん、a5ua さん、ISLeさん
ご指摘ありがとうございます。誠おっしゃる通りです。

そしてzxzyさん。謝ります。本当にすみませんm(_ _)m
忘れてください!!
これならいけるんじゃね?とおもってVC++で動いたときは、これでいいや。と思ったんですが、
あろうことか、基本的な要件を満たせていないのですね。やれやれ。
あやうくzxzyさんを未定義の闇に引きずり込むところでした。

しかし結局特にこれといった注意を払わなくてよくて、安全で
かつzxzyさんの要件を満たすような魔法の方法はないのでしょうかね?
(なんか自分が勉強不足なだけで古今東西議論されつくしてそうな話題な気もしますが。)

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#34

投稿記事 by tk-xleader » 12年前

>ISLeさん

申し訳ありません、確かに、constメンバを含むクラスにプレースメントnewを用いても、それだけでは未定義の操作になるわけではないですね。
プレースメントnewした後でa[0]にアクセスしたらアウトでしょうけれども…

>GRAMさん

考えて見ましたが、そもそもzxzyさんのやろうとしていること自体がかなりイレギュラーです。constメンバを変数初期化後に変更しようということ自体が認められていませんし、プレースメントnewにしても、ISLeさんが提示したコード以上のことをやってしまうと、未定義のコードになっちゃいますし…
単にconstメンバを含むクラスを配列にしたいというだけならば、そもそもsoftya(ソフト屋)さんの提示したコードで解決していますし… intで宣言した変数をconst intに書き換えるのは絶対に不可能ですからね…

zxzy

Re: メンバ変数のconst

#35

投稿記事 by zxzy » 12年前

返信ありがとうございます。

うぅ..C++って難しいですね。
なんか嫌になってきます…


コンパイルエラーに関して

VC++で=をたまたま使わないからだったのですか。
それに比べるとCygwinは使わなくてもチェックしてくれるんですね。

VC++で

コード:

Hoge h1(1);
Hoge h2(2);
h2 = h1;
を試してみたらちゃんとコンパイル時にエラーを確認できました。

vectorの場合は追加時にメモリが足りないと別の場所に確保してコピーするようなので
この方法は使わないほうがいいみたいですね。

a5uaさん
コンパイラが生成するデフォルトの代入演算子ってこんなのなんですね。





プレースメントnewに関して

ISLeさん丁寧な解説ありがとうございます。
lowe さんが書きました:
ISLe さんが書きました:スレを読み直してみたところloweさんが「プレースメントnewは静的に配置される」と書かれてました。
これは間違いですね。
わたしがあげた例の話として「静的なメモリ領域にインスタンスを配置します」と言ったつもりでして、決して静的でなくてはならないと言ったつもりは無いのですが・・・・・。
まぁいずれにせよ誤解を招いたのは私の非ですね、zxzyさんすみませんでしたm(_ _)m
書いていただいたコードのAのメモリを確保するときに
staticがなくA a[10];だけだったので宣言がローカルでもなんでも静的になるのだと勝手に解釈していました。
こちらこそ理解力なくてすみません。
ISLe さんが書きました: 規格上は違反ではないので分かってて使う分には良いのですけどね。
厳しい条件を忘れてうっかりバグを埋め込んでしまわないようになるべく使わないほうが良いと思います。
たいてい他にもっと良い方法があるはずです。
プレースメントnewを使うのはメモリプールを実装するときだけにしておいてください。
そうですね。今回はプレースメントnewを使うのはやめておきます。


GRAM さんが書きました: しかし結局特にこれといった注意を払わなくてよくて、安全で
かつzxzyさんの要件を満たすような魔法の方法はないのでしょうかね?
tkmakwins15 さんが書きました: 考えて見ましたが、そもそもzxzyさんのやろうとしていること自体がかなりイレギュラーです。constメンバを変数初期化後に変更しようということ自体が認められていませんし、プレースメントnewにしても、ISLeさんが提示したコード以上のことをやってしまうと、未定義のコードになっちゃいますし…
単にconstメンバを含むクラスを配列にしたいというだけならば、そもそもsoftya(ソフト屋)さんの提示したコードで解決していますし… intで宣言した変数をconst intに書き換えるのは絶対に不可能ですからね…
softya(ソフト屋)さんに教えていただいたコードでやりたいことはできているのでこれでいこうかなと思います。
でも、いい方法ってこれくらいしかないのですかね。(mapとptr_vectorもコンテナはダメみたいですし)

今回するつもりのことでは使いませんがsoftya(ソフト屋)さんのコードでもconstを使っている以上コピーは
できませんよね。

もうconstなんて使わずただのintにして自分で値を変更しないよう注意すればいいかなとも思えてきました…

メンバ変数に定数を使いたいことってよくあるような気がするのですがそんなイレギュラーだったのですか?


あと、
tkmakwins15 さんが書きました: プレースメントnewした後でa[0]にアクセスしたらアウトでしょうけれども…
をみて思ったのですがプレースメントnewをするとa[0]って配置されたものにアクセスすることになるのでは
ないのですか?a[0]にアクセスして未定義になるなら配置したものにアクセスできないし、
こちらに書かれているコードで

コード:

int main(){
    CSample sample;
    std::cout << sample.getNum()<<std::endl;
    new(&sample)CSample(100);
    std::cout << sample.getNum()<<std::endl;
}
でプレースメントnewのあとにsample.getNum()のアクセスもダメということになるのではないのでしょうか?

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#36

投稿記事 by ISLe » 12年前

zxzy さんが書きました:
tkmakwins15 さんが書きました: プレースメントnewした後でa[0]にアクセスしたらアウトでしょうけれども…
をみて思ったのですがプレースメントnewをするとa[0]って配置されたものにアクセスすることになるのでは
ないのですか?a[0]にアクセスして未定義になるなら配置したものにアクセスできないし、
こちらに書かれているコードで

コード:

int main(){
    CSample sample;
    std::cout << sample.getNum()<<std::endl;
    new(&sample)CSample(100);
    std::cout << sample.getNum()<<std::endl;
}
でプレースメントnewのあとにsample.getNum()のアクセスもダメということになるのではないのでしょうか?
未定義の動作になるのは、定値修飾された非静的(constありstaticなし)メンバを含む場合です。
#他にも条件があります。
tkmakwins15 さんの日記のコードは未定義の動作にならない条件を満たしているので問題ないです。

プレースメントnewの戻り値を使えば再使用された新しいオブジェクトに正しくアクセスできます。

コード:

#include <iostream>
struct A {
    const int a; // 定値修飾された非静的メンバ
    A()      : a(0) { std::cout << "A():" << a << std::endl; }
    A(int a) : a(a) { std::cout << "A():" << a << std::endl; }
    ~A() { std::cout << "~A():" << a << std::endl; }
};
int main(void) {
    A a[1];
    A *p = new(&a[0]) A(1); // 再使用される直前にa[0]の生存範囲は終了したとみなせる
    a[0].a; // a[0]は無効 未定義の動作
    p->a;   // OK
    return 0;
}
未定義の動作というのはたまたま動いてしまう場合も含まれるので気を付けましょう。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#37

投稿記事 by tk-xleader » 12年前

zxzy さんが書きました:メンバ変数に定数を使いたいことってよくあるような気がするのですがそんなイレギュラーだったのですか?
「メンバ変数にconstをつけること」ではなくて、「それをコンストラクタの初期化子以外で初期化しようとすること」がイレギュラーということですね。
初期化以降のコードでconst変数を書き換えると、そもそもconstの意味が分からなくなりますからね。

zxzy

Re: メンバ変数のconst

#38

投稿記事 by zxzy » 12年前

返信ありがとうございます。
ISLe さんが書きました: 未定義の動作になるのは、定値修飾された非静的(constありstaticなし)メンバを含む場合です。
#他にも条件があります。
tkmakwins15 さんの日記のコードは未定義の動作にならない条件を満たしているので問題ないです。

プレースメントnewの戻り値を使えば再使用された新しいオブジェクトに正しくアクセスできます。

コード:

#include <iostream>
struct A {
    const int a; // 定値修飾された非静的メンバ
    A()      : a(0) { std::cout << "A():" << a << std::endl; }
    A(int a) : a(a) { std::cout << "A():" << a << std::endl; }
    ~A() { std::cout << "~A():" << a << std::endl; }
};
int main(void) {
    A a[1];
    A *p = new(&a[0]) A(1); // 再使用される直前にa[0]の生存範囲は終了したとみなせる
    a[0].a; // a[0]は無効 未定義の動作
    p->a;   // OK
    return 0;
}
未定義の動作というのはたまたま動いてしまう場合も含まれるので気を付けましょう。
よくみるとconstがついてないですね。

constありなら返り値を使う必要があるんですね。
ということはプレースメントnewでもアクセスは->になりますね。

でも、どうしてconstありstaticなしのメンバがあると未定義になるのでしょうか?
constをはずすわけではないしintならよくてconst intになるとダメになる理由がわからないです。

tkmakwins15 さんが書きました: 「メンバ変数にconstをつけること」ではなくて、「それをコンストラクタの初期化子以外で初期化しようとすること」がイレギュラーということですね。
初期化以降のコードでconst変数を書き換えると、そもそもconstの意味が分からなくなりますからね。
普通に初期化子で初期化する softya(ソフト屋) さんに教えていただいた方法をとることにします。

constが最初の一回だけ初期化or代入ができるというふうになっていれば使いやすいんですけどね。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#39

投稿記事 by ISLe » 12年前

zxzy さんが書きました:constありなら返り値を使う必要があるんですね。
ということはプレースメントnewでもアクセスは->になりますね。
new演算子の戻り値を必ず使うということで良いのではないでしょうかね。

ポインタと配列は文法上は互換なので->を使う代わりに
p[0].a
と書くこともできますが…。

アロー演算子(->)が使ってあると、このオブジェクトは移動や複写しないように設計されているのだな、というのがその場で分かります。
ドット演算子(.)が使われているとコードをさかのぼらないとポインタなのか配列なのか区別できないのでコードを読むのに手間がかかります。
JavaとかC#とかはドット演算子しかないですけど、クラス型に対してはアロー演算子と同じ作用しかしませんからその場で判断できます。
読みやすいコードというのはコメントの位置とか見た目が揃ってるということではないのですよね。

zxzy さんが書きました:でも、どうしてconstありstaticなしのメンバがあると未定義になるのでしょうか?
constをはずすわけではないしintならよくてconst intになるとダメになる理由がわからないです。
規格にそう書いてあるから、というのが利用する側の立場ですが、C/C++の規格は既存のコンパイラの実装に配慮した内容があちこちにあります。
想像でしかありませんけど、初期化しかできない定値オブジェクトは最適化の影響でメモリ領域の再使用には収まらない可能性があるのではないでしょうかね。
そういう意味ではconst外しも同様の理由によるかと思います。
staticが付いたメンバは静的に割り当てられるので、メモリの再使用の影響を受けません。
zxzy さんが書きました:constが最初の一回だけ初期化or代入ができるというふうになっていれば使いやすいんですけどね。
他の言語ではそういうことができますけど、その代わりに犠牲にしていることもあります。
C/C++は安全性を犠牲にしてでも手に入れたいものがあるひとが使う言語だと思います。
最近はそうでもなくなってきてますけど。

zxzy

Re: メンバ変数のconst

#40

投稿記事 by zxzy » 12年前

ISLe さんが書きました: new演算子の戻り値を必ず使うということで良いのではないでしょうかね。
そうですね。
いつもそうしていればconstがあるからとか考えなくて済みますしね。
ISLe さんが書きました: ポインタと配列は文法上は互換なので->を使う代わりに
p[0].a
と書くこともできますが…。
そういえばそんなこともできましたね。
しかし、そのような書き方だと配列なのかポインタなのかが
わかりづらくなるのでポインタならアロー演算子にします。
ISLe さんが書きました: アロー演算子(->)が使ってあると、このオブジェクトは移動や複写しないように設計されているのだな、というのがその場で分かります。
ドット演算子(.)が使われているとコードをさかのぼらないとポインタなのか配列なのか区別できないのでコードを読むのに手間がかかります。
JavaとかC#とかはドット演算子しかないですけど、クラス型に対してはアロー演算子と同じ作用しかしませんからその場で判断できます。
読みやすいコードというのはコメントの位置とか見た目が揃ってるということではないのですよね。
.のほうが見やすく書くのが楽と思っていましたが->を使ったほうがいいこともあるのですね。
JavaやC#にアロー演算子がないのは知りませんでした。
ポインタ使わないようなとても簡単なものしか作ったことがなかったので…
ISLe さんが書きました: 規格にそう書いてあるから、というのが利用する側の立場ですが、C/C++の規格は既存のコンパイラの実装に配慮した内容があちこちにあります。
想像でしかありませんけど、初期化しかできない定値オブジェクトは最適化の影響でメモリ領域の再使用には収まらない可能性があるのではないでしょうかね。
そういう意味ではconst外しも同様の理由によるかと思います。
staticが付いたメンバは静的に割り当てられるので、メモリの再使用の影響を受けません。
そこはもう規格に書かれていることなのですか。
規格ってちゃんと読んでおくべきものなんでしょうか?
今まで必要なものの使いかただけ調べて使っていたので規格というのをよんだことないがないです。
もし読むべきであるというのでしたら見やすくまとめられたページ(日本語でお願いします)を
教えてもらえますか?

最適化の影響 ですか。そう考えるとなんか納得できたような気がします。

ISLe さんが書きました: 他の言語ではそういうことができますけど、その代わりに犠牲にしていることもあります。
C/C++は安全性を犠牲にしてでも手に入れたいものがあるひとが使う言語だと思います。
最近はそうでもなくなってきてますけど。
プログラムの基本的な言語はCかなと思ってほぼC系(C,C++,C#)しか使えないのですが
やっぱり複数の言語が使えてそのときの目的にあったものを使うという風にできたほうがいいですね。
ここの掲示板の過去ログを見ていてもいろいろな言語を使える人が多いと感じますし。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#41

投稿記事 by softya(ソフト屋) » 12年前

ちゃんとした規格の日本語というとコレでしょう。

オンライン閲覧のみ可能。
「日本工業標準調査会:データベース-JIS詳細表示」
http://www.jisc.go.jp/app/pager?%23jps. ... ISNO=X3014

[補足]C++11は、まだJIS規格になっていません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

zxzy

Re: メンバ変数のconst

#42

投稿記事 by zxzy » 12年前

softya(ソフト屋)さん

教えていただきありがとうございます。

C++11はないのかを聞こうとしたら補足が増えてました。
まだ、できていないのですね。

教えていただいたページなのですが、正誤表は正しく表示できるのですが
規格のほうが表示できません。
Google Chromeで開くと読み込み完了しても白紙になってそのまま10分
程度待っても表示されません。スクロールしてみると、ダウンロードしたものは
表示できないみたいなメッセージボックスが出ます。
FireFoxAuroraとIE7ではファイルが壊れているとメッセージが出ます。

softya(ソフト屋)さんのPCでは正しく表示されていますか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: メンバ変数のconst

#43

投稿記事 by softya(ソフト屋) » 12年前

私は都合でAdobeReader9を使っていますので残念ながら読めません。
最新のAdobeReaderXを使ってみて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: メンバ変数のconst

#44

投稿記事 by ISLe » 12年前

Windows XP + IE8 + Adobe Reader 9
Windows 7 x64 + IE9 + Adobe Reader X
の組み合わせで表示できてます。

それと環境設定の『インターネット』→Webブラウザオプションの『PDFをブラウザに表示』にチェックが入っていないと表示されません。

ファイルが壊れているとのメッセージはこちらでも出ます。
もう一度リンクをクリックすれば表示されないでしょうか。

zxzy

Re: メンバ変数のconst

#45

投稿記事 by zxzy » 12年前

softya(ソフト屋)さん、ISLeさん返信ありがとうございます。

Adobe Reader 9を使っていたのでアップデートしたところ9の最新版9.5になりました。
Adobe Reader 9内からアップデートではXにはならないのですね。

ISLeさんの書き込みをみて、9でも最新版ならできるかと思い、もう一度IEでやってみたところ
ちゃんと表示ができました。

規格というのは思ってたとおりすごく長く、内容は思っていたのとは違った感じでした。
時間のあるときに少しずつ読んでみようと思います。

ありがとうございました。


これで疑問もすべて解決できたので二度目ですが解決済みとしたいと思います。
質問に答えてくれたみなさん本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る